WP Offload S3 Lite - Version 2.3

Version Description

This is a major upgrade that switches to using a custom table for storing data about offloaded Media Library items. Once upgraded you will not be able to downgrade without restoring data from a backup.

Download this release

Release Info

Developer deliciousbrains
Plugin Icon 128x128 WP Offload S3 Lite
Version 2.3
Comparing to
See all releases

Code changes from version 2.2.1 to 2.3

README.md CHANGED
@@ -2,9 +2,9 @@
2
  **Contributors:** bradt, deliciousbrains, ianmjones
3
  **Tags:** uploads, amazon, s3, amazon s3, digitalocean, digitalocean spaces, google cloud storage, gcs, mirror, admin, media, cdn, cloudfront
4
  **Requires at least:** 4.9
5
- **Tested up to:** 5.2
6
  **Requires PHP:** 5.5
7
- **Stable tag:** 2.2.1
8
  **License:** GPLv3
9
 
10
  Copies files to Amazon S3, DigitalOcean Spaces or Google Cloud Storage as they are uploaded to the Media Library. Optionally configure Amazon CloudFront or another CDN for faster delivery.
@@ -75,6 +75,9 @@ If you upgrade to the pro version of [WP Offload Media](https://deliciousbrains.
75
 
76
  ## Upgrade Notice ##
77
 
 
 
 
78
  ### 2.0 ###
79
  This is a major upgrade that introduces support for DigitalOcean Spaces, renames the plugin to WP Offload Media Lite, and coincidentally upgrades some of its database settings. You may not be able to downgrade to WP Offload S3 Lite 1.x after upgrading to WP Offload Media Lite 2.0+.
80
 
@@ -86,6 +89,18 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
86
 
87
  ## Changelog ##
88
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  ### WP Offload Media Lite 2.2.1 - 2019-07-18 ###
90
  * Improvement: Menu option and settings page title now include "Lite"
91
  * Improvement: Remove Files From Server option now warns about media backups when switched on
2
  **Contributors:** bradt, deliciousbrains, ianmjones
3
  **Tags:** uploads, amazon, s3, amazon s3, digitalocean, digitalocean spaces, google cloud storage, gcs, mirror, admin, media, cdn, cloudfront
4
  **Requires at least:** 4.9
5
+ **Tested up to:** 5.3
6
  **Requires PHP:** 5.5
7
+ **Stable tag:** 2.3
8
  **License:** GPLv3
9
 
10
  Copies files to Amazon S3, DigitalOcean Spaces or Google Cloud Storage as they are uploaded to the Media Library. Optionally configure Amazon CloudFront or another CDN for faster delivery.
75
 
76
  ## Upgrade Notice ##
77
 
78
+ ### 2.3 ###
79
+ This is a major upgrade that switches to using a custom table for storing data about offloaded Media Library items. Once upgraded you will not be able to downgrade without restoring data from a backup.
80
+
81
  ### 2.0 ###
82
  This is a major upgrade that introduces support for DigitalOcean Spaces, renames the plugin to WP Offload Media Lite, and coincidentally upgrades some of its database settings. You may not be able to downgrade to WP Offload S3 Lite 1.x after upgrading to WP Offload Media Lite 2.0+.
83
 
89
 
90
  ## Changelog ##
91
 
92
+ ### WP Offload Media Lite 2.3 - 2019-11-12 ###
93
+ * [Release Summary Blog Post](https://deliciousbrains.com/wp-offload-media-2-3-released/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
94
+ * New: Upgrade routine to migrate offload data to custom table
95
+ * New: Support for changed Media Library upload process introduced with WordPress 5.3
96
+ * New: Support for new "-scaled" and "-rotated" images introduced with WordPress 5.3
97
+ * New: Support for customizer changes introduced with WordPress 5.3
98
+ * New: Offload new "original_image" file introduced with WordPress 5.3
99
+ * Improvement: Performance boost during both page display and save
100
+ * Improvement: Better detection of offloaded media URLs during page display
101
+ * Bug fix: New Media Library upload given same local file name as offloaded and removed file after Remove Files From Server turned off
102
+ * Bug fix: PHP message: PHP Deprecated: strpos(): Non-string needles will be interpreted as strings in the future
103
+
104
  ### WP Offload Media Lite 2.2.1 - 2019-07-18 ###
105
  * Improvement: Menu option and settings page title now include "Lite"
106
  * Improvement: Remove Files From Server option now warns about media backups when switched on
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -1,5 +1,6 @@
1
  <?php
2
 
 
3
  use DeliciousBrains\WP_Offload_Media\Providers\AWS_Provider;
4
  use DeliciousBrains\WP_Offload_Media\Providers\DigitalOcean_Provider;
5
  use DeliciousBrains\WP_Offload_Media\Providers\GCP_Provider;
@@ -9,6 +10,7 @@ use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Content_Replace_URLs;
9
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_EDD_Replace_URLs;
10
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_File_Sizes;
11
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Filter_Post_Excerpt;
 
12
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Meta_WP_Error;
13
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Region_Meta;
14
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_WPOS3_To_AS3CF;
@@ -107,7 +109,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
107
  'WPOS3_SETTINGS',
108
  );
109
 
110
- const LATEST_UPGRADE_ROUTINE = 7;
111
 
112
  /**
113
  * @param string $plugin_file_path
@@ -142,6 +144,8 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
142
  GCP_Provider::get_provider_key_name() => 'DeliciousBrains\WP_Offload_Media\Providers\GCP_Provider',
143
  );
144
 
 
 
145
  $this->set_provider();
146
 
147
  // Bundled SDK may require AWS setup before data migrations.
@@ -154,6 +158,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
154
  new Upgrade_EDD_Replace_URLs( $this );
155
  new Upgrade_Filter_Post_Excerpt( $this );
156
  new Upgrade_WPOS3_To_AS3CF( $this );
 
157
 
158
  // Plugin setup
159
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
@@ -306,13 +311,22 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
306
  return apply_filters( 'as3cf_settings_menu_title', $this->plugin_menu_title );
307
  }
308
 
 
 
 
 
 
 
 
 
 
309
  /**
310
  * Get the plugin prefix in slug format, ie. replace underscores with hyphens
311
  *
312
  * @return string
313
  */
314
- function get_plugin_prefix_slug() {
315
- return str_replace( '_', '-', $this->plugin_prefix );
316
  }
317
 
318
  /**
@@ -338,7 +352,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
338
  'key' => $key,
339
  'disabled' => false,
340
  'disabled_attr' => '',
341
- 'tr_class' => str_replace( '_', '-', $this->plugin_prefix . '-' . $key . '-container' ),
342
  'setting_msg' => '',
343
  'is_defined' => false,
344
  );
@@ -959,23 +973,38 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
959
  /**
960
  * Removes an attachment's files from provider.
961
  *
962
- * @param int $post_id
963
- * @param array $provider_object
964
- * @param bool $remove_backup_sizes remove previous edited image versions
965
- * @param bool $log_error
966
- * @param bool $return_on_error
967
- * @param bool $force_new_provider_client if we are deleting in bulk, force new provider client
968
- * to cope with possible different regions
969
- */
970
- function remove_attachment_files_from_provider( $post_id, $provider_object, $remove_backup_sizes = true, $log_error = false, $return_on_error = false, $force_new_provider_client = false ) {
971
- $prefix = $this->normalize_object_prefix( $provider_object['key'] );
972
- $bucket = $provider_object['bucket'];
973
- $region = $this->get_provider_object_region( $provider_object );
974
- $paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, false, $remove_backup_sizes );
975
- $paths = apply_filters( 'as3cf_remove_attachment_paths', $paths, $post_id, $provider_object, $remove_backup_sizes );
976
 
977
- if ( is_wp_error( $region ) ) {
978
- $region = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
  }
980
 
981
  $objects_to_remove = array();
@@ -987,7 +1016,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
987
  }
988
 
989
  // finally delete the objects from provider
990
- $this->delete_objects( $region, $bucket, $objects_to_remove, $log_error, $return_on_error, $force_new_provider_client );
991
  }
992
 
993
  /**
@@ -1002,7 +1031,9 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1002
  return;
1003
  }
1004
 
1005
- if ( ! ( $provider_object = $this->get_attachment_provider_info( $post_id ) ) ) {
 
 
1006
  return;
1007
  }
1008
 
@@ -1010,9 +1041,9 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1010
  return;
1011
  }
1012
 
1013
- $this->remove_attachment_files_from_provider( $post_id, $provider_object, true, true, true, $force_new_provider_client );
1014
 
1015
- delete_post_meta( $post_id, 'amazonS3_info' );
1016
  }
1017
 
1018
  /**
@@ -1030,25 +1061,36 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1030
  return $data;
1031
  }
1032
 
1033
- if ( ! ( $old_provider_object = $this->get_attachment_provider_info( $post_id ) ) && ! $this->get_setting( 'copy-to-s3' ) ) {
 
 
 
 
 
 
 
1034
  // abort if not already uploaded to provider and the copy setting is off
1035
  return $data;
1036
  }
1037
 
 
 
 
 
1038
  // allow provider upload to be cancelled for any reason
1039
- $pre = apply_filters( 'as3cf_pre_update_attachment_metadata', false, $data, $post_id, $old_provider_object );
1040
  if ( false !== $pre ) {
1041
  return $data;
1042
  }
1043
 
1044
  // upload attachment to provider
1045
- $provider_meta = $this->upload_attachment( $post_id, $data );
1046
 
1047
- if ( is_wp_error( $provider_meta ) ) {
1048
  return $data;
1049
  }
1050
 
1051
- return $provider_meta;
1052
  }
1053
 
1054
  /**
@@ -1061,10 +1103,12 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1061
  * to cope with possible different regions
1062
  * @param bool $remove_local_files
1063
  *
1064
- * @return array|WP_Error $provider_object|$meta If meta is supplied, return it. Else return provider meta
1065
  * @throws Exception
1066
  */
1067
  public function upload_attachment( $post_id, $data = null, $file_path = null, $force_new_provider_client = false, $remove_local_files = true ) {
 
 
1068
  $return_metadata = null;
1069
  if ( is_null( $data ) ) {
1070
  $data = wp_get_attachment_metadata( $post_id, true );
@@ -1092,15 +1136,82 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1092
  $file_path = get_attached_file( $post_id, true );
1093
  }
1094
 
1095
- // Check file exists locally before attempting upload
1096
- if ( ! file_exists( $file_path ) ) {
1097
- $error_msg = sprintf( __( 'File %s does not exist', 'amazon-s3-and-cloudfront' ), $file_path );
1098
 
1099
  return $this->return_upload_error( $error_msg, $return_metadata );
1100
  }
1101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1102
  // Get original file's stats.
1103
- $filesize = filesize( $file_path );
1104
  $file_name = wp_basename( $file_path );
1105
  $type = get_post_mime_type( $post_id );
1106
  $allowed_types = $this->get_allowed_mime_types();
@@ -1115,23 +1226,25 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1115
  $acl = $this->get_provider()->get_default_acl();
1116
 
1117
  // check the attachment already exists in provider, eg. edit or restore image
1118
- if ( ( $old_provider_object = $this->get_attachment_provider_info( $post_id ) ) ) {
1119
  // Must be offloaded to same provider as currently configured.
1120
  if ( ! $this->is_attachment_served_by_provider( $post_id, true ) ) {
1121
  return $this->return_upload_error( __( 'Already offloaded to a different provider', 'amazon-s3-and-cloudfront' ), $return_metadata );
1122
  }
1123
 
1124
- // use existing non default ACL if attachment already exists
1125
- if ( isset( $old_provider_object['acl'] ) ) {
1126
- $acl = $old_provider_object['acl'];
1127
  }
1128
 
1129
  // use existing prefix
1130
- $prefix = $this->normalize_object_prefix( $old_provider_object['key'] );
1131
  // use existing bucket
1132
- $bucket = $old_provider_object['bucket'];
1133
  // get existing region
1134
- $region = isset( $old_provider_object['region'] ) ? $old_provider_object['region'] : '';
 
 
1135
  } else {
1136
  // derive prefix from various settings
1137
  $time = $this->get_attachment_folder_year_month( $post_id, $data );
@@ -1143,6 +1256,9 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1143
  if ( is_wp_error( $region ) ) {
1144
  $region = '';
1145
  }
 
 
 
1146
  }
1147
 
1148
  $acl = apply_filters( 'wps3_upload_acl', $acl, $type, $data, $post_id, $this ); // Old naming convention, will be deprecated soon
@@ -1167,43 +1283,43 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1167
  $args['ContentEncoding'] = 'gzip';
1168
  }
1169
 
1170
- $image_size = wp_attachment_is_image( $post_id ) ? 'full' : '';
1171
- $args = apply_filters( 'as3cf_object_meta', $args, $post_id, $image_size, false );
1172
- $provider_object = array(
1173
- 'provider' => $this->get_provider()->get_provider_key_name(),
1174
- 'region' => $bucket !== $args['Bucket'] ? $this->get_bucket_region( $args['Bucket'], true ) : $region,
1175
- 'bucket' => $args['Bucket'],
1176
- 'key' => $args['Key'],
1177
- 'acl' => $args['ACL'],
1178
- );
1179
 
1180
- // Do not store object ACL if set to the default value.
1181
- if ( $provider_object['acl'] === $this->get_provider()->get_default_acl() ) {
1182
- unset( $provider_object['acl'] );
1183
- }
 
 
 
1184
 
1185
- do_action( 'as3cf_upload_attachment_pre_remove', $post_id, $provider_object, $prefix, $args );
1186
 
 
1187
  $files_to_remove = array();
1188
 
1189
- $provider_client = $this->get_provider_client( $provider_object['region'], $force_new_provider_client );
1190
 
1191
- try {
1192
- $provider_client->upload_object( $args );
1193
- $files_to_remove[] = $file_path;
1194
- } catch ( Exception $e ) {
1195
- $error_msg = sprintf( __( 'Error offloading %s to provider: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
1196
 
1197
- return $this->return_upload_error( $error_msg, $return_metadata );
1198
- }
1199
 
1200
- delete_post_meta( $post_id, 'amazonS3_info' );
 
 
 
1201
 
1202
- add_post_meta( $post_id, 'amazonS3_info', $provider_object );
 
 
1203
 
1204
- $file_paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, $data );
1205
- $additional_images = array();
1206
- $provider_object_sizes = array();
1207
 
1208
  foreach ( $file_paths as $size => $file_path ) {
1209
  if ( ! in_array( $file_path, $files_to_remove ) ) {
@@ -1216,8 +1332,8 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1216
  'ContentType' => $this->get_mime_type( $file_path ),
1217
  );
1218
 
1219
- if ( $this->get_provider()->get_default_acl() !== $acl ) {
1220
- $provider_object_sizes[ $size ]['acl'] = $acl;
1221
  }
1222
  }
1223
  }
@@ -1225,42 +1341,72 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1225
  $upload_errors = array();
1226
 
1227
  foreach ( $additional_images as $size => $image ) {
 
 
 
 
 
1228
  $args = apply_filters( 'as3cf_object_meta', array_merge( $args, $image ), $post_id, $size, false );
1229
 
1230
  if ( ! file_exists( $args['SourceFile'] ) ) {
1231
- $upload_errors[] = $this->return_upload_error( sprintf( __( 'File %s does not exist', 'amazon-s3-and-cloudfront' ), $args['SourceFile'] ) );
 
 
1232
  continue;
1233
  }
1234
 
1235
  try {
 
1236
  $provider_client->upload_object( $args );
1237
- $files_to_remove[] = $image['SourceFile'];
 
 
 
1238
  } catch ( Exception $e ) {
1239
  $upload_errors[] = $this->return_upload_error( sprintf( __( 'Error offloading %s to provider: %s', 'amazon-s3-and-cloudfront' ), $args['SourceFile'], $e->getMessage() ) );
1240
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1241
  }
1242
 
1243
  $remove_local_files_setting = $this->get_setting( 'remove-local-file' );
1244
 
1245
- if ( $remove_local_files ) {
1246
- if ( $remove_local_files_setting ) {
1247
- // Allow other functions to remove files after they have processed
1248
- $files_to_remove = apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $post_id, $file_path );
1249
 
1250
- // Remove duplicates
1251
- $files_to_remove = array_unique( $files_to_remove );
1252
 
1253
- // Delete the files and record original file's size before removal.
1254
- $this->remove_local_files( $files_to_remove, $post_id );
 
 
 
 
1255
 
1256
- // Store filesize in the attachment meta data for use by WP
1257
- if ( 0 < $filesize ) {
1258
- $data['filesize'] = $filesize;
1259
 
1260
- if ( is_null( $return_metadata ) ) {
1261
- // Update metadata with filesize
1262
- update_post_meta( $post_id, '_wp_attachment_metadata', $data );
1263
- }
1264
  }
1265
  }
1266
  }
@@ -1270,16 +1416,36 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1270
  $data = $this->maybe_cleanup_filesize_metadata( $post_id, $data, empty( $return_metadata ) );
1271
  }
1272
 
1273
- if ( ! empty( $provider_object_sizes ) ) {
1274
- // Additional image sizes have custom ACLs, update meta
1275
- $provider_object['sizes'] = $provider_object_sizes;
1276
- update_post_meta( $post_id, 'amazonS3_info', $provider_object );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1277
  }
1278
 
1279
  // Keep track of attachments uploaded by this instance.
1280
  $this->uploaded_post_ids[] = $post_id;
1281
 
1282
- do_action( 'as3cf_post_upload_attachment', $post_id, $provider_object );
1283
 
1284
  if ( $upload_errors ) {
1285
  return $this->consolidate_upload_errors( $upload_errors );
@@ -1290,7 +1456,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1290
  return $data;
1291
  }
1292
 
1293
- return $provider_object;
1294
  }
1295
 
1296
  /**
@@ -1373,11 +1539,14 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1373
  /**
1374
  * Remove files from the local site, recording total filesize in meta if attachment ID given.
1375
  *
1376
- * @param array $file_paths array of files to remove
1377
- * @param int $attachment_id
 
1378
  */
1379
- function remove_local_files( $file_paths, $attachment_id = 0 ) {
1380
- $filesize_total = 0;
 
 
1381
 
1382
  foreach ( $file_paths as $index => $path ) {
1383
  if ( ! empty( $attachment_id ) && is_int( $attachment_id ) ) {
@@ -1405,7 +1574,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1405
  }
1406
 
1407
  // If we were able to sum up file sizes for an attachment, record it.
1408
- if ( $filesize_total > 0 ) {
1409
  update_post_meta( $attachment_id, 'as3cf_filesize_total', $filesize_total );
1410
  }
1411
  }
@@ -1526,7 +1695,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1526
  * @return string
1527
  */
1528
  public function filter_unique_filename( $filename, $ext, $dir, $post_id = null ) {
1529
- if ( ! $this->get_setting( 'copy-to-s3' ) || ! $this->is_plugin_setup( true ) ) {
1530
  return $filename;
1531
  }
1532
 
@@ -1619,6 +1788,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1619
  }
1620
  $file = $path . $filename;
1621
 
 
1622
  $sql = $wpdb->prepare( "
1623
  SELECT COUNT(*)
1624
  FROM $wpdb->postmeta
@@ -1626,7 +1796,16 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1626
  AND meta_value = %s
1627
  ", '_wp_attached_file', $file );
1628
 
1629
- return (bool) $wpdb->get_var( $sql );
 
 
 
 
 
 
 
 
 
1630
  }
1631
 
1632
  /**
@@ -1664,44 +1843,24 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1664
  */
1665
  function generate_unique_filename( $name, $ext, $time ) {
1666
  $count = 1;
1667
- $filename = $name . $count . $ext;
1668
 
1669
  while ( $this->does_file_exist( $filename, $time ) ) {
1670
  $count++;
1671
- $filename = $name . $count . $ext;
1672
  }
1673
 
1674
  return $filename;
1675
  }
1676
 
1677
- /**
1678
- * Get attachment provider info
1679
- *
1680
- * @param int $post_id
1681
- *
1682
- * @return mixed
1683
- */
1684
- public function get_attachment_provider_info( $post_id ) {
1685
- $provider_object = get_post_meta( $post_id, 'amazonS3_info', true );
1686
-
1687
- if ( is_array( $provider_object ) ) {
1688
- $provider_object = array_merge( array(
1689
- 'provider' => static::$default_provider,
1690
- 'region' => null,
1691
- ), $provider_object );
1692
- }
1693
-
1694
- $provider_object = apply_filters( 'as3cf_get_attachment_s3_info', $provider_object, $post_id ); // Backwards compatibility
1695
-
1696
- return apply_filters( 'as3cf_get_attachment_provider_info', $provider_object, $post_id );
1697
- }
1698
-
1699
  /**
1700
  * Check the plugin is correctly setup
1701
  *
1702
  * @param bool $with_credentials Do provider credentials need to be set up too? Defaults to false.
1703
  *
1704
  * @return bool
 
 
1705
  */
1706
  function is_plugin_setup( $with_credentials = false ) {
1707
  if ( $with_credentials && $this->get_provider()->needs_access_keys() ) {
@@ -1831,11 +1990,11 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1831
  * @return bool|mixed|WP_Error
1832
  */
1833
  public function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null, $headers = array(), $skip_rewrite_check = false ) {
1834
- if ( ! ( $provider_object = $this->is_attachment_served_by_provider( $post_id, $skip_rewrite_check ) ) ) {
1835
  return false;
1836
  }
1837
 
1838
- $url = $this->get_attachment_provider_url( $post_id, $provider_object, $expires, $size, $meta, $headers );
1839
 
1840
  return apply_filters( 'as3cf_wp_get_attachment_url', $url, $post_id );
1841
  }
@@ -1908,20 +2067,20 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1908
  /**
1909
  * Get the provider URL for an attachment
1910
  *
1911
- * @param int $post_id
1912
- * @param array $provider_object
1913
- * @param null|int $expires
1914
- * @param null|string|array $size
1915
- * @param null|array $meta
1916
- * @param array $headers
1917
  *
1918
- * @return mixed|WP_Error
1919
  */
1920
- public function get_attachment_provider_url( $post_id, $provider_object, $expires = null, $size = null, $meta = null, $headers = array() ) {
1921
- // We don't use $this->get_provider_object_region() here because we don't want
1922
- // to make an AWS API call and slow down page loading
1923
- if ( isset( $provider_object['region'] ) && ( $this->get_provider()->region_required() || $this->get_provider()->get_default_region() !== $provider_object['region'] ) ) {
1924
- $region = $this->get_provider()->sanitize_region( $provider_object['region'] );
1925
  } else {
1926
  $region = '';
1927
  }
@@ -1930,12 +2089,12 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1930
 
1931
  // Force use of secured URL when ACL has been set to private
1932
  if ( is_null( $expires ) ) {
1933
- if ( is_null( $size ) && isset( $provider_object['acl'] ) && $this->get_provider()->get_private_acl() === $provider_object['acl'] ) {
1934
  // Full size URL private
1935
  $expires = self::DEFAULT_EXPIRES;
1936
  }
1937
 
1938
- if ( ! is_null( $size ) && isset( $provider_object['sizes'][ $size ]['acl'] ) && $this->get_provider()->get_private_acl() === $provider_object['sizes'][ $size ]['acl'] ) {
1939
  // Alternative size URL private
1940
  $expires = self::DEFAULT_EXPIRES;
1941
  }
@@ -1951,15 +2110,15 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1951
  }
1952
 
1953
  if ( ! empty( $meta ) && isset( $meta['sizes'][ $size ]['file'] ) ) {
1954
- $size_prefix = dirname( $provider_object['key'] );
1955
  $size_file_prefix = ( '.' === $size_prefix ) ? '' : $size_prefix . '/';
1956
 
1957
- $provider_object['key'] = $size_file_prefix . $meta['sizes'][ $size ]['file'];
1958
  }
1959
  }
1960
 
1961
  $scheme = $this->get_url_scheme();
1962
- $domain = $this->get_provider()->get_url_domain( $provider_object['bucket'], $region, $expires );
1963
  $base_url = $scheme . '://' . $domain;
1964
 
1965
  if ( ! is_null( $expires ) && $this->is_plugin_setup( true ) ) {
@@ -1970,20 +2129,20 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1970
 
1971
  $expires = time() + apply_filters( 'as3cf_expires', $expires );
1972
  $secure_url = $this->get_provider_client( $region )
1973
- ->get_object_url( $provider_object['bucket'], $provider_object['key'], $expires, $headers );
1974
 
1975
- return apply_filters( 'as3cf_get_attachment_secure_url', $secure_url, $provider_object, $post_id, $expires, $headers );
1976
  } catch ( Exception $e ) {
1977
  return new WP_Error( 'exception', $e->getMessage() );
1978
  }
1979
  }
1980
 
1981
- $provider_object['key'] = $this->maybe_update_cloudfront_path( $provider_object['key'] );
1982
 
1983
- $file = $this->encode_filename_in_path( $provider_object['key'] );
1984
  $url = $base_url . '/' . $file;
1985
 
1986
- return apply_filters( 'as3cf_get_attachment_url', $url, $provider_object, $post_id, $expires, $headers );
1987
  }
1988
 
1989
  /**
@@ -1995,6 +2154,10 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
1995
  * @return bool|mixed|WP_Error
1996
  */
1997
  public function wp_get_attachment_url( $url, $post_id ) {
 
 
 
 
1998
  $new_url = $this->get_attachment_url( $post_id );
1999
 
2000
  if ( false === $new_url ) {
@@ -2020,7 +2183,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2020
  * @return string
2021
  */
2022
  public function maybe_encode_get_image_tag( $html, $id, $alt, $title, $align, $size ) {
2023
- if ( ! ( $provider_object = $this->is_attachment_served_by_provider( $id ) ) ) {
2024
  // Not served by provider, return
2025
  return $html;
2026
  }
@@ -2037,7 +2200,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2037
  }
2038
 
2039
  $img_src = $matches[1];
2040
- $new_img_src = $this->maybe_sign_intermediate_size( $img_src, $id, $size, $provider_object );
2041
  $new_img_src = $this->encode_filename_in_path( $new_img_src );
2042
 
2043
  return str_replace( $img_src, $new_img_src, $html );
@@ -2054,13 +2217,13 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2054
  * @return array
2055
  */
2056
  public function maybe_encode_wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
2057
- if ( ! ( $provider_object = $this->is_attachment_served_by_provider( $attachment_id ) ) ) {
2058
  // Not served by provider, return
2059
  return $image;
2060
  }
2061
 
2062
  if ( isset( $image[0] ) ) {
2063
- $url = $this->maybe_sign_intermediate_size( $image[0], $attachment_id, $size, $provider_object );
2064
  $url = $this->encode_filename_in_path( $url );
2065
 
2066
  $image[0] = $url;
@@ -2079,7 +2242,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2079
  * @return array
2080
  */
2081
  public function maybe_encode_wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
2082
- if ( ! ( $provider_object = $this->is_attachment_served_by_provider( $attachment->ID ) ) ) {
2083
  // Not served by provider, return
2084
  return $response;
2085
  }
@@ -2090,7 +2253,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2090
 
2091
  if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
2092
  foreach ( $response['sizes'] as $size => $value ) {
2093
- $url = $this->maybe_sign_intermediate_size( $value['url'], $attachment->ID, $size, $provider_object );
2094
  $url = $this->encode_filename_in_path( $url );
2095
 
2096
  $response['sizes'][ $size ]['url'] = $url;
@@ -2110,13 +2273,13 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2110
  * @return array
2111
  */
2112
  public function maybe_encode_image_get_intermediate_size( $data, $post_id, $size ) {
2113
- if ( ! ( $provider_object = $this->is_attachment_served_by_provider( $post_id ) ) ) {
2114
  // Not served by provider, return
2115
  return $data;
2116
  }
2117
 
2118
  if ( isset( $data['url'] ) ) {
2119
- $url = $this->maybe_sign_intermediate_size( $data['url'], $post_id, $size, $provider_object );
2120
  $url = $this->encode_filename_in_path( $url );
2121
 
2122
  $data['url'] = $url;
@@ -2128,23 +2291,23 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2128
  /**
2129
  * Sign intermediate size.
2130
  *
2131
- * @param string $url
2132
- * @param int $attachment_id
2133
- * @param string|array $size
2134
- * @param bool|array $provider_object
2135
  *
2136
- * @return mixed|WP_Error
2137
  */
2138
- protected function maybe_sign_intermediate_size( $url, $attachment_id, $size, $provider_object = false ) {
2139
- if ( ! $provider_object ) {
2140
- $provider_object = $this->get_attachment_provider_info( $attachment_id );
2141
  }
2142
 
2143
  $size = $this->maybe_convert_size_to_string( $attachment_id, $size );
2144
 
2145
- if ( isset( $provider_object['sizes'][ $size ] ) ) {
2146
  // Private file, add AWS signature if required
2147
- return $this->get_attachment_provider_url( $attachment_id, $provider_object, null, $size );
2148
  }
2149
 
2150
  return $url;
@@ -2222,7 +2385,7 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2222
  * @param bool $skip_current_provider_check Skip checking if offloaded to current provider. Default: false, negated if $provider supplied
2223
  * @param Provider|null $provider Provider where attachment expected to be offloaded to. Default: currently configured provider
2224
  *
2225
- * @return bool|array
2226
  */
2227
  public function is_attachment_served_by_provider( $attachment_id, $skip_rewrite_check = false, $skip_current_provider_check = false, Provider $provider = null ) {
2228
  if ( ! $skip_rewrite_check && ! $this->get_setting( 'serve-from-s3' ) ) {
@@ -2230,7 +2393,9 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2230
  return false;
2231
  }
2232
 
2233
- if ( ! ( $provider_object = $this->get_attachment_provider_info( $attachment_id ) ) ) {
 
 
2234
  // File not uploaded to a provider
2235
  return false;
2236
  }
@@ -2239,12 +2404,12 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2239
  $provider = $this->get_provider();
2240
  }
2241
 
2242
- if ( ! empty( $provider ) && $provider::get_provider_key_name() !== $provider_object['provider'] ) {
2243
  // File not uploaded to required provider
2244
  return false;
2245
  }
2246
 
2247
- return $provider_object;
2248
  }
2249
 
2250
  /**
@@ -2285,33 +2450,6 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2285
  return str_replace( $file_name, $encoded_file_name, $file );
2286
  }
2287
 
2288
- /**
2289
- * Decode file name.
2290
- *
2291
- * @param string $file
2292
- *
2293
- * @return string
2294
- */
2295
- public function decode_filename_in_path( $file ) {
2296
- $url = parse_url( $file );
2297
-
2298
- if ( ! isset( $url['path'] ) ) {
2299
- // Can't determine path, return original
2300
- return $file;
2301
- }
2302
-
2303
- $file_name = wp_basename( $url['path'] );
2304
-
2305
- if ( false === strpos( $file_name, '%' ) ) {
2306
- // File name not encoded, return original
2307
- return $file;
2308
- }
2309
-
2310
- $decoded_file_name = rawurldecode( $file_name );
2311
-
2312
- return str_replace( $file_name, $decoded_file_name, $file );
2313
- }
2314
-
2315
  /**
2316
  * Allow processes to update the file on provider via update_attached_file()
2317
  *
@@ -2325,11 +2463,13 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2325
  return $file;
2326
  }
2327
 
2328
- if ( ! ( $provider_object = $this->get_attachment_provider_info( $attachment_id ) ) ) {
 
 
2329
  return $file;
2330
  }
2331
 
2332
- $file = apply_filters( 'as3cf_update_attached_file', $file, $attachment_id, $provider_object );
2333
 
2334
  return $file;
2335
  }
@@ -2345,14 +2485,14 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2345
  * @return string
2346
  */
2347
  function get_attached_file( $file, $attachment_id ) {
2348
- if ( file_exists( $file ) || ! ( $provider_object = $this->is_attachment_served_by_provider( $attachment_id ) ) ) {
2349
  return $file;
2350
  }
2351
 
2352
  $url = $this->get_attachment_url( $attachment_id );
2353
 
2354
  // return the URL by default
2355
- $file = apply_filters( 'as3cf_get_attached_file', $url, $file, $attachment_id, $provider_object );
2356
 
2357
  return $file;
2358
  }
@@ -2580,6 +2720,15 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2580
  );
2581
  }
2582
 
 
 
 
 
 
 
 
 
 
2583
  /**
2584
  * Returns the Provider's default region slug.
2585
  *
@@ -2679,34 +2828,6 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
2679
  return $region;
2680
  }
2681
 
2682
- /**
2683
- * Get the region of the bucket stored in the provider metadata.
2684
- *
2685
- *
2686
- * @param array $provider_object
2687
- * @param int $post_id - if supplied will update the provider meta if no region found
2688
- *
2689
- * @return string|WP_Error - region name
2690
- */
2691
- function get_provider_object_region( $provider_object, $post_id = null ) {
2692
- if ( ! isset( $provider_object['region'] ) ) {
2693
- // if region hasn't been stored in the provider metadata retrieve using the bucket
2694
- $region = $this->get_bucket_region( $provider_object['bucket'], true );
2695
- if ( is_wp_error( $region ) ) {
2696
- return $region;
2697
- }
2698
-
2699
- $provider_object['region'] = $region;
2700
-
2701
- if ( ! is_null( $post_id ) ) {
2702
- // retrospectively update provider metadata with region
2703
- update_post_meta( $post_id, 'amazonS3_info', $provider_object );
2704
- }
2705
- }
2706
-
2707
- return $provider_object['region'];
2708
- }
2709
-
2710
  /**
2711
  * AJAX handler for get_buckets()
2712
  */
@@ -3407,56 +3528,63 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
3407
  /**
3408
  * Apply ACL to an attachment and associated files
3409
  *
3410
- * @param int $post_id
3411
- * @param array $provider_object
3412
- * @param string $acl
3413
  *
3414
- * @return array|bool|WP_Error
3415
- * @throws Exception
3416
  */
3417
- public function set_attachment_acl_on_provider( $post_id, $provider_object, $acl ) {
3418
  // Return early if already set to the desired ACL
3419
- if ( isset( $provider_object['acl'] ) && $acl === $provider_object['acl'] ) {
3420
  return false;
3421
  }
3422
 
 
 
3423
  $args = array(
3424
  'ACL' => $acl,
3425
- 'Bucket' => $provider_object['bucket'],
3426
- 'Key' => $provider_object['key'],
3427
  );
3428
 
3429
- $region = ( isset( $provider_object['region'] ) ) ? $provider_object['region'] : false;
3430
- $provider_client = $this->get_provider_client( $region, true );
3431
 
3432
  try {
 
3433
  $provider_client->update_object_acl( $args );
3434
- $provider_object['acl'] = $acl;
3435
-
3436
- // update S3 meta data
3437
- if ( $acl == $this->get_provider()->get_default_acl() ) {
3438
- unset( $provider_object['acl'] );
3439
- }
3440
 
3441
- update_post_meta( $post_id, 'amazonS3_info', $provider_object );
 
 
 
 
 
 
 
 
 
 
 
 
3442
  } catch ( Exception $e ) {
3443
- $msg = 'Error setting ACL to ' . $acl . ' for ' . $provider_object['key'] . ': ' . $e->getMessage();
3444
  AS3CF_Error::log( $msg );
3445
 
3446
  return new WP_Error( 'acl_exception', $msg );
3447
  }
3448
 
3449
- return $provider_object;
3450
  }
3451
 
3452
  /**
3453
  * Make admin notice for when object ACL has changed
3454
  *
3455
- * @param array $provider_object
3456
  */
3457
- function make_acl_admin_notice( $provider_object ) {
3458
- $filename = wp_basename( $provider_object['key'] );
3459
- $acl = ( isset( $provider_object['acl'] ) ) ? $provider_object['acl'] : $this->get_provider()->get_default_acl();
3460
  $acl_name = $this->get_acl_display_name( $acl );
3461
  $text = sprintf( __( '<strong>WP Offload Media</strong> &mdash; The file %s has been given %s permissions in the bucket.', 'amazon-s3-and-cloudfront' ), "<strong>{$filename}</strong>", "<strong>{$acl_name}</strong>" );
3462
 
@@ -3727,13 +3855,19 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
3727
  $media_counts = $this->media_counts();
3728
 
3729
  $output .= 'Media Files: ';
3730
- $output .= number_format_i18n( $media_counts['total'] );
3731
  $output .= "\r\n";
3732
 
3733
  $output .= 'Offloaded Media Files: ';
3734
- $output .= number_format_i18n( $media_counts['offloaded'] );
3735
  $output .= "\r\n";
3736
 
 
 
 
 
 
 
3737
  $output .= 'Number of Image Sizes: ';
3738
  $sizes = count( get_intermediate_image_sizes() );
3739
  $output .= number_format_i18n( $sizes );
@@ -4283,55 +4417,6 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
4283
  return intval( $memory_limit ) * 1024 * 1024;
4284
  }
4285
 
4286
- /**
4287
- * Count attachments on a site.
4288
- *
4289
- * @param string $prefix
4290
- * @param bool $skip_transient Whether to force database query and skip transient, default false
4291
- * @param bool $force Whether to force database query and skip static cache, implies $skip_transient, default false
4292
- *
4293
- * @return array Keys:
4294
- * total: Total media count for site (prefix)
4295
- * offloaded: Count of offloaded media for site (prefix)
4296
- * not_offloaded: Difference between total and offloaded
4297
- */
4298
- public function count_attachments( $prefix, $skip_transient = false, $force = false ) {
4299
- global $wpdb;
4300
-
4301
- static $counts;
4302
- static $skips;
4303
-
4304
- $transient_key = 'as3cf_' . $prefix . '_attachment_counts';
4305
-
4306
- // Been here, done it, won't do it again!
4307
- // Well, unless this is the first transient skip for the prefix, then we need to do it.
4308
- if ( ! $force && ! empty( $counts[ $transient_key ] ) && ( false === $skip_transient || ! empty( $skips[ $transient_key ] ) ) ) {
4309
- return $counts[ $transient_key ];
4310
- }
4311
-
4312
- if ( $force || $skip_transient || false === ( $attachment_counts = get_site_transient( $transient_key ) ) ) {
4313
- $sql = "
4314
- SELECT COUNT(DISTINCT p.`ID`) total, COUNT(DISTINCT pm.`post_id`) offloaded
4315
- FROM `{$prefix}posts` p
4316
- LEFT OUTER JOIN `{$prefix}postmeta` pm ON p.`ID` = pm.`post_id` AND pm.`meta_key` = 'amazonS3_info'
4317
- WHERE p.`post_type` = 'attachment'
4318
- ";
4319
-
4320
- $attachment_counts = $wpdb->get_row( $sql, ARRAY_A );
4321
-
4322
- $attachment_counts['not_offloaded'] = $attachment_counts['total'] - $attachment_counts['offloaded'];
4323
-
4324
- set_site_transient( $transient_key, $attachment_counts, 2 * MINUTE_IN_SECONDS );
4325
-
4326
- // One way or another we've skipped the transient.
4327
- $skips[ $transient_key ] = true;
4328
- }
4329
-
4330
- $counts[ $transient_key ] = $attachment_counts;
4331
-
4332
- return $attachment_counts;
4333
- }
4334
-
4335
  /**
4336
  * Get the total attachment and total offloaded/not offloaded attachment counts
4337
  *
@@ -4342,22 +4427,35 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
4342
  */
4343
  public function media_counts( $skip_transient = false, $force = false ) {
4344
  if ( $skip_transient || false === ( $attachment_counts = get_site_transient( 'as3cf_attachment_counts' ) ) ) {
4345
- $table_prefixes = $this->get_all_blog_table_prefixes();
4346
- $total = 0;
4347
- $offloaded = 0;
4348
- $not_offloaded = 0;
 
 
 
4349
 
4350
  foreach ( $table_prefixes as $blog_id => $table_prefix ) {
4351
- $counts = $this->count_attachments( $table_prefix, $skip_transient, $force );
4352
- $total += $counts['total'];
4353
- $offloaded += $counts['offloaded'];
4354
- $not_offloaded += $counts['not_offloaded'];
 
 
 
 
 
 
 
4355
  }
4356
 
4357
  $attachment_counts = array(
4358
- 'total' => $total,
4359
- 'offloaded' => $offloaded,
4360
- 'not_offloaded' => $not_offloaded,
 
 
 
4361
  );
4362
 
4363
  set_site_transient( 'as3cf_attachment_counts', $attachment_counts, 2 * MINUTE_IN_SECONDS );
@@ -4615,42 +4713,39 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
4615
  }
4616
 
4617
  /**
4618
- * Return a formatted S3 info with display friendly defaults
4619
  *
4620
- * @param int $id
4621
- * @param array|null $provider_object
4622
  *
4623
- * @return array
4624
  */
4625
- public function get_formatted_provider_info( $id, $provider_object = null ) {
4626
- if ( is_null( $provider_object ) ) {
4627
- if ( ! ( $provider_object = $this->get_attachment_provider_info( $id ) ) ) {
4628
- return false;
4629
- }
4630
  }
4631
 
4632
- $provider_object['url'] = $this->get_attachment_provider_url( $id, $provider_object );
 
 
 
 
4633
 
4634
- $acl = ( isset( $provider_object['acl'] ) ) ? $provider_object['acl'] : $this->get_provider()->get_default_acl();
4635
  $acl_info = array(
4636
  'acl' => $acl,
4637
  'name' => $this->get_acl_display_name( $acl ),
4638
  'title' => $this->get_media_action_strings( 'change_to_private' ),
4639
  );
4640
 
4641
- if ( $this->get_provider()->get_private_acl() === $acl ) {
4642
  $acl_info['title'] = $this->get_media_action_strings( 'change_to_public' );
4643
  }
4644
 
4645
- $provider_object['acl'] = $acl_info;
4646
-
4647
- if ( isset( $provider_object['region'] ) ) {
4648
- $provider_object['region'] = $this->get_provider()->get_region_name( $provider_object['region'] );
4649
- }
4650
-
4651
- if ( ! empty( $provider_object['provider'] ) ) {
4652
- $provider_object['provider_name'] = $this->get_provider_service_name( $provider_object['provider'] );
4653
- }
4654
 
4655
  return $provider_object;
4656
  }
@@ -4764,19 +4859,6 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
4764
  return reset( $parts );
4765
  }
4766
 
4767
- /**
4768
- * Normalize object prefix.
4769
- *
4770
- * @param string $prefix
4771
- *
4772
- * @return string
4773
- */
4774
- protected function normalize_object_prefix( $prefix ) {
4775
- $directory = dirname( $prefix );
4776
-
4777
- return ( '.' === $directory ) ? '' : $directory . '/';
4778
- }
4779
-
4780
  /**
4781
  * Has the given attachment been uploaded by this instance?
4782
  *
@@ -4839,14 +4921,10 @@ class Amazon_S3_And_CloudFront extends AS3CF_Plugin_Base {
4839
  * @return string
4840
  */
4841
  public function get_acl_for_intermediate_size( $attachment_id, $size ) {
4842
- $provider_info = $this->get_attachment_provider_info( $attachment_id );
4843
-
4844
- if ( 'original' === $size || empty( $size ) ) {
4845
- return isset( $provider_info['acl'] ) ? $provider_info['acl'] : $this->get_provider()->get_default_acl();
4846
- }
4847
 
4848
- if ( ! empty( $provider_info['sizes'][ $size ]['acl'] ) ) {
4849
- return $provider_info['sizes'][ $size ]['acl'];
4850
  }
4851
 
4852
  return $this->get_provider()->get_default_acl();
1
  <?php
2
 
3
+ use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
4
  use DeliciousBrains\WP_Offload_Media\Providers\AWS_Provider;
5
  use DeliciousBrains\WP_Offload_Media\Providers\DigitalOcean_Provider;
6
  use DeliciousBrains\WP_Offload_Media\Providers\GCP_Provider;
10
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_EDD_Replace_URLs;
11
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_File_Sizes;
12
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Filter_Post_Excerpt;
13
+ use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Items_Table;
14
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Meta_WP_Error;
15
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_Region_Meta;
16
  use DeliciousBrains\WP_Offload_Media\Upgrades\Upgrade_WPOS3_To_AS3CF;
109
  'WPOS3_SETTINGS',
110
  );
111
 
112
+ const LATEST_UPGRADE_ROUTINE = 8;
113
 
114
  /**
115
  * @param string $plugin_file_path
144
  GCP_Provider::get_provider_key_name() => 'DeliciousBrains\WP_Offload_Media\Providers\GCP_Provider',
145
  );
146
 
147
+ Media_Library_Item::init_cache();
148
+
149
  $this->set_provider();
150
 
151
  // Bundled SDK may require AWS setup before data migrations.
158
  new Upgrade_EDD_Replace_URLs( $this );
159
  new Upgrade_Filter_Post_Excerpt( $this );
160
  new Upgrade_WPOS3_To_AS3CF( $this );
161
+ new Upgrade_Items_Table( $this );
162
 
163
  // Plugin setup
164
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
311
  return apply_filters( 'as3cf_settings_menu_title', $this->plugin_menu_title );
312
  }
313
 
314
+ /**
315
+ * Get the plugin prefix.
316
+ *
317
+ * @return string
318
+ */
319
+ public function get_plugin_prefix() {
320
+ return $this->plugin_prefix;
321
+ }
322
+
323
  /**
324
  * Get the plugin prefix in slug format, ie. replace underscores with hyphens
325
  *
326
  * @return string
327
  */
328
+ public function get_plugin_prefix_slug() {
329
+ return str_replace( '_', '-', $this->get_plugin_prefix() );
330
  }
331
 
332
  /**
352
  'key' => $key,
353
  'disabled' => false,
354
  'disabled_attr' => '',
355
+ 'tr_class' => str_replace( '_', '-', $this->get_plugin_prefix() . '-' . $key . '-container' ),
356
  'setting_msg' => '',
357
  'is_defined' => false,
358
  );
973
  /**
974
  * Removes an attachment's files from provider.
975
  *
976
+ * @param int $post_id
977
+ * @param Media_Library_Item $as3cf_item
978
+ * @param bool $include_backups remove previous edited image versions
979
+ * @param bool $log_error
980
+ * @param bool $return_on_error
981
+ * @param bool $force_new_provider_client if we are deleting in bulk, force new provider client
982
+ * to cope with possible different regions
983
+ */
984
+ function remove_attachment_files_from_provider( $post_id, Media_Library_Item $as3cf_item, $include_backups = true, $log_error = false, $return_on_error = false, $force_new_provider_client = false ) {
985
+ $prefix = $as3cf_item->normalized_path_dir();
986
+ $paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, false, $include_backups );
987
+ $paths = apply_filters( 'as3cf_remove_attachment_paths', $paths, $post_id, $as3cf_item, $include_backups );
 
 
988
 
989
+ // If another item in current site shares full size *local* paths, only remove remote files not referenced by duplicates.
990
+ // We reference local paths as they should be reflected one way or another remotely, including backups.
991
+ $fullsize_paths = AS3CF_Utils::fullsize_paths( $paths );
992
+ $as3cf_items_with_paths = Media_Library_Item::get_by_source_path( $fullsize_paths, array( $post_id ), false );
993
+
994
+ $duplicate_paths = array();
995
+
996
+ foreach ( $as3cf_items_with_paths as $as3cf_item_with_path ) {
997
+ /* @var Media_Library_Item $as3cf_item_with_path */
998
+ $duplicate_paths += array_values( AS3CF_Utils::get_attachment_file_paths( $as3cf_item_with_path->source_id(), false, false, $include_backups ) );
999
+ }
1000
+
1001
+ if ( ! empty( $duplicate_paths ) ) {
1002
+ $paths = array_diff( $paths, $duplicate_paths );
1003
+ }
1004
+
1005
+ // Nothing to do, shortcut out.
1006
+ if ( empty( $paths ) ) {
1007
+ return;
1008
  }
1009
 
1010
  $objects_to_remove = array();
1016
  }
1017
 
1018
  // finally delete the objects from provider
1019
+ $this->delete_objects( $as3cf_item->region(), $as3cf_item->bucket(), $objects_to_remove, $log_error, $return_on_error, $force_new_provider_client );
1020
  }
1021
 
1022
  /**
1031
  return;
1032
  }
1033
 
1034
+ $as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
1035
+
1036
+ if ( ! $as3cf_item ) {
1037
  return;
1038
  }
1039
 
1041
  return;
1042
  }
1043
 
1044
+ $this->remove_attachment_files_from_provider( $post_id, $as3cf_item, true, true, true, $force_new_provider_client );
1045
 
1046
+ $as3cf_item->delete();
1047
  }
1048
 
1049
  /**
1061
  return $data;
1062
  }
1063
 
1064
+ // Protect against updates of partially formed metadata.
1065
+ if ( wp_attachment_is_image( $post_id ) && empty( $data['sizes'] ) ) {
1066
+ return $data;
1067
+ }
1068
+
1069
+ $as3cf_item = Media_Library_Item::get_by_source_id( $post_id );
1070
+
1071
+ if ( ! $as3cf_item && ! $this->get_setting( 'copy-to-s3' ) ) {
1072
  // abort if not already uploaded to provider and the copy setting is off
1073
  return $data;
1074
  }
1075
 
1076
+ if ( empty( $as3cf_item ) ) {
1077
+ $as3cf_item = null;
1078
+ }
1079
+
1080
  // allow provider upload to be cancelled for any reason
1081
+ $pre = apply_filters( 'as3cf_pre_update_attachment_metadata', false, $data, $post_id, $as3cf_item );
1082
  if ( false !== $pre ) {
1083
  return $data;
1084
  }
1085
 
1086
  // upload attachment to provider
1087
+ $attachment_metadata = $this->upload_attachment( $post_id, $data );
1088
 
1089
+ if ( is_wp_error( $attachment_metadata ) ) {
1090
  return $data;
1091
  }
1092
 
1093
+ return $attachment_metadata;
1094
  }
1095
 
1096
  /**
1103
  * to cope with possible different regions
1104
  * @param bool $remove_local_files
1105
  *
1106
+ * @return array|Media_Library_Item|WP_Error
1107
  * @throws Exception
1108
  */
1109
  public function upload_attachment( $post_id, $data = null, $file_path = null, $force_new_provider_client = false, $remove_local_files = true ) {
1110
+ static $offloaded = array();
1111
+
1112
  $return_metadata = null;
1113
  if ( is_null( $data ) ) {
1114
  $data = wp_get_attachment_metadata( $post_id, true );
1136
  $file_path = get_attached_file( $post_id, true );
1137
  }
1138
 
1139
+ // Check for valid "full" file path before attempting upload
1140
+ if ( empty( $file_path ) ) {
1141
+ $error_msg = sprintf( __( 'Media Library item with ID %d does not have a valid file path', 'amazon-s3-and-cloudfront' ), $post_id );
1142
 
1143
  return $this->return_upload_error( $error_msg, $return_metadata );
1144
  }
1145
 
1146
+ $offload_full = true;
1147
+ $old_item = Media_Library_Item::get_by_source_id( $post_id );
1148
+
1149
+ // If item not already offloaded, is it a duplicate?
1150
+ $duplicate = false;
1151
+ if ( empty( $old_item ) ) {
1152
+ $old_items = Media_Library_Item::get_by_source_path( $file_path, $post_id, true, true );
1153
+
1154
+ if ( ! empty( $old_items[0] ) ) {
1155
+ $duplicate = true;
1156
+
1157
+ /** @var Media_Library_Item $duplicate_item */
1158
+ $duplicate_item = $old_items[0];
1159
+
1160
+ $old_item = new Media_Library_Item(
1161
+ $duplicate_item->provider(),
1162
+ $duplicate_item->region(),
1163
+ $duplicate_item->bucket(),
1164
+ $duplicate_item->path(),
1165
+ $duplicate_item->is_private(),
1166
+ $post_id,
1167
+ $duplicate_item->source_path(),
1168
+ wp_basename( $duplicate_item->original_source_path() ),
1169
+ $duplicate_item->private_sizes()
1170
+ );
1171
+
1172
+ $old_item->save();
1173
+
1174
+ // If original offloaded in same process, skip offloading anything it's already processed.
1175
+ // Otherwise, do not need to offload full file if duplicate and file missing.
1176
+ if ( ! empty( $offloaded[ $duplicate_item->id() ] ) ) {
1177
+ $offloaded[ $old_item->id() ] = $offloaded[ $duplicate_item->id() ];
1178
+ } elseif ( ! file_exists( $file_path ) ) {
1179
+ $offload_full = false;
1180
+ }
1181
+
1182
+ unset( $old_items, $duplicate_item );
1183
+ }
1184
+ }
1185
+
1186
+ // If not already offloaded in request, check full file exists locally before attempting offload.
1187
+ if ( $offload_full ) {
1188
+ if ( $old_item && ! empty( $offloaded[ $old_item->id() ][ $file_path ] ) ) {
1189
+ $offload_full = false;
1190
+ } elseif ( ! file_exists( $file_path ) ) {
1191
+ $error_msg = sprintf( __( 'File %s does not exist', 'amazon-s3-and-cloudfront' ), $file_path );
1192
+
1193
+ return $this->return_upload_error( $error_msg, $return_metadata );
1194
+ }
1195
+ }
1196
+
1197
+ $file_paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, $data );
1198
+ $file_paths = array_diff( $file_paths, array( $file_path ) );
1199
+
1200
+ // Are there any files not already offloaded if full already offloaded in this request?
1201
+ if ( false === $offload_full ) {
1202
+ if ( empty( $file_paths ) ) {
1203
+ return $return_metadata;
1204
+ }
1205
+
1206
+ $offloaded_file_paths = empty( $offloaded[ $old_item->id() ] ) ? array() : $offloaded[ $old_item->id() ];
1207
+ unset( $offloaded_file_paths[ $file_path ] );
1208
+
1209
+ if ( ! empty( $offloaded_file_paths ) && empty( array_diff( $file_paths, array_keys( $offloaded_file_paths ) ) ) ) {
1210
+ return $return_metadata;
1211
+ }
1212
+ }
1213
+
1214
  // Get original file's stats.
 
1215
  $file_name = wp_basename( $file_path );
1216
  $type = get_post_mime_type( $post_id );
1217
  $allowed_types = $this->get_allowed_mime_types();
1226
  $acl = $this->get_provider()->get_default_acl();
1227
 
1228
  // check the attachment already exists in provider, eg. edit or restore image
1229
+ if ( $old_item ) {
1230
  // Must be offloaded to same provider as currently configured.
1231
  if ( ! $this->is_attachment_served_by_provider( $post_id, true ) ) {
1232
  return $this->return_upload_error( __( 'Already offloaded to a different provider', 'amazon-s3-and-cloudfront' ), $return_metadata );
1233
  }
1234
 
1235
+ // Use private ACL if existing offload is already private.
1236
+ if ( $old_item->is_private() ) {
1237
+ $acl = $this->get_provider()->get_private_acl();
1238
  }
1239
 
1240
  // use existing prefix
1241
+ $prefix = $old_item->normalized_path_dir();
1242
  // use existing bucket
1243
+ $bucket = $old_item->bucket();
1244
  // get existing region
1245
+ $region = $old_item->region();
1246
+ // Get existing original filename.
1247
+ $original_filename = wp_basename( $old_item->original_source_path() );
1248
  } else {
1249
  // derive prefix from various settings
1250
  $time = $this->get_attachment_folder_year_month( $post_id, $data );
1256
  if ( is_wp_error( $region ) ) {
1257
  $region = '';
1258
  }
1259
+
1260
+ // There may be an original image that can override the default original filename.
1261
+ $original_filename = empty( $data['original_image'] ) ? null : $data['original_image'];
1262
  }
1263
 
1264
  $acl = apply_filters( 'wps3_upload_acl', $acl, $type, $data, $post_id, $this ); // Old naming convention, will be deprecated soon
1283
  $args['ContentEncoding'] = 'gzip';
1284
  }
1285
 
1286
+ $image_size = wp_attachment_is_image( $post_id ) ? 'full' : '';
1287
+ $args = apply_filters( 'as3cf_object_meta', $args, $post_id, $image_size, false );
 
 
 
 
 
 
 
1288
 
1289
+ $provider = $this->get_provider()->get_provider_key_name();
1290
+ $region = $bucket !== $args['Bucket'] ? $this->get_bucket_region( $args['Bucket'], true ) : $region;
1291
+ $is_private = $this->get_provider()->get_private_acl() === $args['ACL'] ? true : false;
1292
+ $private_sizes = empty( $old_item ) ? array() : $old_item->private_sizes();
1293
+ $item_id = empty( $old_item ) ? null : $old_item->id();
1294
+
1295
+ $as3cf_item = new Media_Library_Item( $provider, $region, $args['Bucket'], $args['Key'], $is_private, $post_id, $file_path, $original_filename, $private_sizes, $item_id );
1296
 
1297
+ do_action( 'as3cf_upload_attachment_pre_remove', $post_id, $as3cf_item, $prefix, $args );
1298
 
1299
+ $new_offloads = array();
1300
  $files_to_remove = array();
1301
 
1302
+ $provider_client = $this->get_provider_client( $as3cf_item->region(), $force_new_provider_client );
1303
 
1304
+ if ( $offload_full ) {
1305
+ try {
1306
+ // May raise exception, so don't offload anything else if there's an error.
1307
+ $filesize = (int) filesize( $file_path );
 
1308
 
1309
+ // May raise exception, so don't offload anything else if there's an error.
1310
+ $provider_client->upload_object( $args );
1311
 
1312
+ $new_offloads[ $file_path ] = $filesize; // Note: pre `as3cf_object_meta` filter value.
1313
+ $files_to_remove[] = $file_path; // Note: pre `as3cf_object_meta` filter value.
1314
+ } catch ( Exception $e ) {
1315
+ $error_msg = sprintf( __( 'Error offloading %s to provider: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
1316
 
1317
+ return $this->return_upload_error( $error_msg, $return_metadata );
1318
+ }
1319
+ }
1320
 
1321
+ $additional_images = array();
1322
+ $private_sizes = array(); // Reset private sizes to be as expected at time of (re)upload.
 
1323
 
1324
  foreach ( $file_paths as $size => $file_path ) {
1325
  if ( ! in_array( $file_path, $files_to_remove ) ) {
1332
  'ContentType' => $this->get_mime_type( $file_path ),
1333
  );
1334
 
1335
+ if ( $this->get_provider()->get_private_acl() === $acl ) {
1336
+ $private_sizes[] = $size;
1337
  }
1338
  }
1339
  }
1341
  $upload_errors = array();
1342
 
1343
  foreach ( $additional_images as $size => $image ) {
1344
+ // If this file has already been offloaded during this request, skip actual offload.
1345
+ if ( $old_item && ! empty( $offloaded[ $old_item->id() ][ $image['SourceFile'] ] ) ) {
1346
+ continue;
1347
+ }
1348
+
1349
  $args = apply_filters( 'as3cf_object_meta', array_merge( $args, $image ), $post_id, $size, false );
1350
 
1351
  if ( ! file_exists( $args['SourceFile'] ) ) {
1352
+ if ( ! $duplicate ) {
1353
+ $upload_errors[] = $this->return_upload_error( sprintf( __( 'File %s does not exist', 'amazon-s3-and-cloudfront' ), $args['SourceFile'] ) );
1354
+ }
1355
  continue;
1356
  }
1357
 
1358
  try {
1359
+ // May raise exception, but for sizes we'll just log it and maybe try again later if called.
1360
  $provider_client->upload_object( $args );
1361
+ $files_to_remove[] = $image['SourceFile']; // Note: pre `as3cf_object_meta` filter value.
1362
+
1363
+ // May raise exception, we'll log that, and carry on anyway.
1364
+ $new_offloads[ $image['SourceFile'] ] = (int) filesize( $image['SourceFile'] ); // Note: pre `as3cf_object_meta` filter value.
1365
  } catch ( Exception $e ) {
1366
  $upload_errors[] = $this->return_upload_error( sprintf( __( 'Error offloading %s to provider: %s', 'amazon-s3-and-cloudfront' ), $args['SourceFile'], $e->getMessage() ) );
1367
  }
1368
+
1369
+ // Edge Case: If previously uploaded and a different original_image wasn't picked up but is now, record it.
1370
+ // This is most likely to happen if older version of plugin was used with WP5.3 and large or rotated image auto-created.
1371
+ if ( 'original_image' === $size && wp_basename( $as3cf_item->original_source_path() ) !== wp_basename( $image['SourceFile'] ) ) {
1372
+ $as3cf_item = new Media_Library_Item(
1373
+ $as3cf_item->provider(),
1374
+ $as3cf_item->region(),
1375
+ $as3cf_item->bucket(),
1376
+ $as3cf_item->path(),
1377
+ $as3cf_item->is_private(),
1378
+ $as3cf_item->source_id(),
1379
+ $as3cf_item->source_path(),
1380
+ wp_basename( $image['SourceFile'] ),
1381
+ $as3cf_item->private_sizes(),
1382
+ $as3cf_item->id()
1383
+ );
1384
+ }
1385
  }
1386
 
1387
  $remove_local_files_setting = $this->get_setting( 'remove-local-file' );
1388
 
1389
+ if ( $remove_local_files && $remove_local_files_setting ) {
1390
+ // Allow other functions to remove files after they have processed
1391
+ $files_to_remove = apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $post_id, $file_path );
 
1392
 
1393
+ // Remove duplicates
1394
+ $files_to_remove = array_unique( $files_to_remove );
1395
 
1396
+ $filesize_total = 0;
1397
+ if ( ! empty( $old_item ) && ! empty( $offloaded[ $old_item->id() ] ) ) {
1398
+ $filesize_total = array_sum( $offloaded[ $old_item->id() ] );
1399
+ }
1400
+ // Delete the files and record original file's size before removal.
1401
+ $this->remove_local_files( $files_to_remove, $post_id, $filesize_total );
1402
 
1403
+ // Store filesize in the attachment meta data for use by WP if we've just offloaded the full size file.
1404
+ if ( ! empty( $filesize ) ) {
1405
+ $data['filesize'] = $filesize;
1406
 
1407
+ if ( is_null( $return_metadata ) ) {
1408
+ // Update metadata with filesize
1409
+ update_post_meta( $post_id, '_wp_attachment_metadata', $data );
 
1410
  }
1411
  }
1412
  }
1416
  $data = $this->maybe_cleanup_filesize_metadata( $post_id, $data, empty( $return_metadata ) );
1417
  }
1418
 
1419
+ // Additional image sizes have custom ACLs, record them.
1420
+ if ( ! empty( $private_sizes ) ) {
1421
+ $as3cf_item = new Media_Library_Item(
1422
+ $as3cf_item->provider(),
1423
+ $as3cf_item->region(),
1424
+ $as3cf_item->bucket(),
1425
+ $as3cf_item->path(),
1426
+ $as3cf_item->is_private(),
1427
+ $as3cf_item->source_id(),
1428
+ $as3cf_item->source_path(),
1429
+ wp_basename( $as3cf_item->original_source_path() ),
1430
+ $private_sizes,
1431
+ $as3cf_item->id()
1432
+ );
1433
+ }
1434
+
1435
+ // All done, save record of offloaded item.
1436
+ $as3cf_item->save();
1437
+
1438
+ // Keep track of individual files offloaded during this request.
1439
+ if ( empty( $offloaded[ $as3cf_item->id() ] ) ) {
1440
+ $offloaded[ $as3cf_item->id() ] = $new_offloads;
1441
+ } else {
1442
+ $offloaded[ $as3cf_item->id() ] += $new_offloads;
1443
  }
1444
 
1445
  // Keep track of attachments uploaded by this instance.
1446
  $this->uploaded_post_ids[] = $post_id;
1447
 
1448
+ do_action( 'as3cf_post_upload_attachment', $post_id, $as3cf_item );
1449
 
1450
  if ( $upload_errors ) {
1451
  return $this->consolidate_upload_errors( $upload_errors );
1456
  return $data;
1457
  }
1458
 
1459
+ return $as3cf_item;
1460
  }
1461
 
1462
  /**
1539
  /**
1540
  * Remove files from the local site, recording total filesize in meta if attachment ID given.
1541
  *
1542
+ * @param array $file_paths Files to remove.
1543
+ * @param int $attachment_id Optional, if supplied filesize metadata recorded.
1544
+ * @param int $filesize_total Optional, if removing partial set of an attachment's files, pass in previously removed total.
1545
  */
1546
+ function remove_local_files( $file_paths, $attachment_id = 0, $filesize_total = 0 ) {
1547
+ if ( empty( $filesize_total ) ) {
1548
+ $filesize_total = 0;
1549
+ }
1550
 
1551
  foreach ( $file_paths as $index => $path ) {
1552
  if ( ! empty( $attachment_id ) && is_int( $attachment_id ) ) {
1574
  }
1575
 
1576
  // If we were able to sum up file sizes for an attachment, record it.
1577
+ if ( ! empty( $attachment_id ) && is_int( $attachment_id ) && $filesize_total > 0 ) {
1578
  update_post_meta( $attachment_id, 'as3cf_filesize_total', $filesize_total );
1579
  }
1580
  }
1695
  * @return string
1696
  */
1697
  public function filter_unique_filename( $filename, $ext, $dir, $post_id = null ) {
1698
+ if ( ! $this->is_plugin_setup( true ) ) {
1699
  return $filename;
1700
  }
1701
 
1788
  }
1789
  $file = $path . $filename;
1790
 
1791
+ // WordPress doesn't check its own basic record, so we will.
1792
  $sql = $wpdb->prepare( "
1793
  SELECT COUNT(*)
1794
  FROM $wpdb->postmeta
1796
  AND meta_value = %s
1797
  ", '_wp_attached_file', $file );
1798
 
1799
+ if ( (bool) $wpdb->get_var( $sql ) ) {
1800
+ return true;
1801
+ }
1802
+
1803
+ // Check our records of local source path as it also covers original_image.
1804
+ if ( ! empty( Media_Library_Item::get_by_source_path( array( $file ), array(), true, true ) ) ) {
1805
+ return true;
1806
+ }
1807
+
1808
+ return false;
1809
  }
1810
 
1811
  /**
1843
  */
1844
  function generate_unique_filename( $name, $ext, $time ) {
1845
  $count = 1;
1846
+ $filename = $name . '-' . $count . $ext;
1847
 
1848
  while ( $this->does_file_exist( $filename, $time ) ) {
1849
  $count++;
1850
+ $filename = $name . '-' . $count . $ext;
1851
  }
1852
 
1853
  return $filename;
1854
  }
1855
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1856
  /**
1857
  * Check the plugin is correctly setup
1858
  *
1859
  * @param bool $with_credentials Do provider credentials need to be set up too? Defaults to false.
1860
  *
1861
  * @return bool
1862
+ *
1863
+ * TODO: Performance - cache / static var by param.
1864
  */
1865
  function is_plugin_setup( $with_credentials = false ) {
1866
  if ( $with_credentials && $this->get_provider()->needs_access_keys() ) {
1990
  * @return bool|mixed|WP_Error
1991
  */
1992
  public function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null, $headers = array(), $skip_rewrite_check = false ) {
1993
+ if ( ! ( $as3cf_item = $this->is_attachment_served_by_provider( $post_id, $skip_rewrite_check ) ) ) {
1994
  return false;
1995
  }
1996
 
1997
+ $url = $this->get_attachment_provider_url( $post_id, $as3cf_item, $expires, $size, $meta, $headers );
1998
 
1999
  return apply_filters( 'as3cf_wp_get_attachment_url', $url, $post_id );
2000
  }
2067
  /**
2068
  * Get the provider URL for an attachment
2069
  *
2070
+ * @param int $post_id
2071
+ * @param Media_Library_Item $as3cf_item
2072
+ * @param null|int $expires
2073
+ * @param null|string|array $size
2074
+ * @param null|array $meta
2075
+ * @param array $headers
2076
  *
2077
+ * @return string|WP_Error
2078
  */
2079
+ public function get_attachment_provider_url( $post_id, Media_Library_Item $as3cf_item, $expires = null, $size = null, $meta = null, $headers = array() ) {
2080
+ $item_path = $as3cf_item->path();
2081
+
2082
+ if ( ! empty( $as3cf_item->region() ) && ( $this->get_provider()->region_required() || $this->get_provider()->get_default_region() !== $as3cf_item->region() ) ) {
2083
+ $region = $this->get_provider()->sanitize_region( $as3cf_item->region() );
2084
  } else {
2085
  $region = '';
2086
  }
2089
 
2090
  // Force use of secured URL when ACL has been set to private
2091
  if ( is_null( $expires ) ) {
2092
+ if ( is_null( $size ) && $as3cf_item->is_private() ) {
2093
  // Full size URL private
2094
  $expires = self::DEFAULT_EXPIRES;
2095
  }
2096
 
2097
+ if ( ! is_null( $size ) && $as3cf_item->is_private_size( $size ) ) {
2098
  // Alternative size URL private
2099
  $expires = self::DEFAULT_EXPIRES;
2100
  }
2110
  }
2111
 
2112
  if ( ! empty( $meta ) && isset( $meta['sizes'][ $size ]['file'] ) ) {
2113
+ $size_prefix = dirname( $item_path );
2114
  $size_file_prefix = ( '.' === $size_prefix ) ? '' : $size_prefix . '/';
2115
 
2116
+ $item_path = $size_file_prefix . $meta['sizes'][ $size ]['file'];
2117
  }
2118
  }
2119
 
2120
  $scheme = $this->get_url_scheme();
2121
+ $domain = $this->get_provider()->get_url_domain( $as3cf_item->bucket(), $region, $expires );
2122
  $base_url = $scheme . '://' . $domain;
2123
 
2124
  if ( ! is_null( $expires ) && $this->is_plugin_setup( true ) ) {
2129
 
2130
  $expires = time() + apply_filters( 'as3cf_expires', $expires );
2131
  $secure_url = $this->get_provider_client( $region )
2132
+ ->get_object_url( $as3cf_item->bucket(), $item_path, $expires, $headers );
2133
 
2134
+ return apply_filters( 'as3cf_get_attachment_secure_url', $secure_url, $as3cf_item, $post_id, $expires, $headers );
2135
  } catch ( Exception $e ) {
2136
  return new WP_Error( 'exception', $e->getMessage() );
2137
  }
2138
  }
2139
 
2140
+ $item_path = $this->maybe_update_cloudfront_path( $item_path );
2141
 
2142
+ $file = $this->encode_filename_in_path( $item_path );
2143
  $url = $base_url . '/' . $file;
2144
 
2145
+ return apply_filters( 'as3cf_get_attachment_url', $url, $as3cf_item, $post_id, $expires, $headers );
2146
  }
2147
 
2148
  /**
2154
  * @return bool|mixed|WP_Error
2155
  */
2156
  public function wp_get_attachment_url( $url, $post_id ) {
2157
+ if ( $this->plugin_compat->is_customizer_crop_action() ) {
2158
+ return $url;
2159
+ }
2160
+
2161
  $new_url = $this->get_attachment_url( $post_id );
2162
 
2163
  if ( false === $new_url ) {
2183
  * @return string
2184
  */
2185
  public function maybe_encode_get_image_tag( $html, $id, $alt, $title, $align, $size ) {
2186
+ if ( ! ( $as3cf_item = $this->is_attachment_served_by_provider( $id ) ) ) {
2187
  // Not served by provider, return
2188
  return $html;
2189
  }
2200
  }
2201
 
2202
  $img_src = $matches[1];
2203
+ $new_img_src = $this->maybe_sign_intermediate_size( $img_src, $id, $size, $as3cf_item );
2204
  $new_img_src = $this->encode_filename_in_path( $new_img_src );
2205
 
2206
  return str_replace( $img_src, $new_img_src, $html );
2217
  * @return array
2218
  */
2219
  public function maybe_encode_wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
2220
+ if ( ! ( $as3cf_item = $this->is_attachment_served_by_provider( $attachment_id ) ) ) {
2221
  // Not served by provider, return
2222
  return $image;
2223
  }
2224
 
2225
  if ( isset( $image[0] ) ) {
2226
+ $url = $this->maybe_sign_intermediate_size( $image[0], $attachment_id, $size, $as3cf_item );
2227
  $url = $this->encode_filename_in_path( $url );
2228
 
2229
  $image[0] = $url;
2242
  * @return array
2243
  */
2244
  public function maybe_encode_wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
2245
+ if ( ! ( $as3cf_item = $this->is_attachment_served_by_provider( $attachment->ID ) ) ) {
2246
  // Not served by provider, return
2247
  return $response;
2248
  }
2253
 
2254
  if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
2255
  foreach ( $response['sizes'] as $size => $value ) {
2256
+ $url = $this->maybe_sign_intermediate_size( $value['url'], $attachment->ID, $size, $as3cf_item );
2257
  $url = $this->encode_filename_in_path( $url );
2258
 
2259
  $response['sizes'][ $size ]['url'] = $url;
2273
  * @return array
2274
  */
2275
  public function maybe_encode_image_get_intermediate_size( $data, $post_id, $size ) {
2276
+ if ( ! ( $as3cf_item = $this->is_attachment_served_by_provider( $post_id ) ) ) {
2277
  // Not served by provider, return
2278
  return $data;
2279
  }
2280
 
2281
  if ( isset( $data['url'] ) ) {
2282
+ $url = $this->maybe_sign_intermediate_size( $data['url'], $post_id, $size, $as3cf_item );
2283
  $url = $this->encode_filename_in_path( $url );
2284
 
2285
  $data['url'] = $url;
2291
  /**
2292
  * Sign intermediate size.
2293
  *
2294
+ * @param string $url
2295
+ * @param int $attachment_id
2296
+ * @param string|array $size
2297
+ * @param bool|Media_Library_Item $as3cf_item
2298
  *
2299
+ * @return string|WP_Error
2300
  */
2301
+ protected function maybe_sign_intermediate_size( $url, $attachment_id, $size, $as3cf_item = false ) {
2302
+ if ( ! $as3cf_item ) {
2303
+ $as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
2304
  }
2305
 
2306
  $size = $this->maybe_convert_size_to_string( $attachment_id, $size );
2307
 
2308
+ if ( $as3cf_item->is_private_size( $size ) ) {
2309
  // Private file, add AWS signature if required
2310
+ return $this->get_attachment_provider_url( $attachment_id, $as3cf_item, null, $size );
2311
  }
2312
 
2313
  return $url;
2385
  * @param bool $skip_current_provider_check Skip checking if offloaded to current provider. Default: false, negated if $provider supplied
2386
  * @param Provider|null $provider Provider where attachment expected to be offloaded to. Default: currently configured provider
2387
  *
2388
+ * @return bool|Media_Library_Item
2389
  */
2390
  public function is_attachment_served_by_provider( $attachment_id, $skip_rewrite_check = false, $skip_current_provider_check = false, Provider $provider = null ) {
2391
  if ( ! $skip_rewrite_check && ! $this->get_setting( 'serve-from-s3' ) ) {
2393
  return false;
2394
  }
2395
 
2396
+ $as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
2397
+
2398
+ if ( ! $as3cf_item ) {
2399
  // File not uploaded to a provider
2400
  return false;
2401
  }
2404
  $provider = $this->get_provider();
2405
  }
2406
 
2407
+ if ( ! empty( $provider ) && $provider::get_provider_key_name() !== $as3cf_item->provider() ) {
2408
  // File not uploaded to required provider
2409
  return false;
2410
  }
2411
 
2412
+ return $as3cf_item;
2413
  }
2414
 
2415
  /**
2450
  return str_replace( $file_name, $encoded_file_name, $file );
2451
  }
2452
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2453
  /**
2454
  * Allow processes to update the file on provider via update_attached_file()
2455
  *
2463
  return $file;
2464
  }
2465
 
2466
+ $as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
2467
+
2468
+ if ( ! $as3cf_item ) {
2469
  return $file;
2470
  }
2471
 
2472
+ $file = apply_filters( 'as3cf_update_attached_file', $file, $attachment_id, $as3cf_item );
2473
 
2474
  return $file;
2475
  }
2485
  * @return string
2486
  */
2487
  function get_attached_file( $file, $attachment_id ) {
2488
+ if ( file_exists( $file ) || ! ( $as3cf_item = $this->is_attachment_served_by_provider( $attachment_id ) ) ) {
2489
  return $file;
2490
  }
2491
 
2492
  $url = $this->get_attachment_url( $attachment_id );
2493
 
2494
  // return the URL by default
2495
+ $file = apply_filters( 'as3cf_get_attached_file', $url, $file, $attachment_id, $as3cf_item );
2496
 
2497
  return $file;
2498
  }
2720
  );
2721
  }
2722
 
2723
+ /**
2724
+ * What is the default provider for legacy data?
2725
+ *
2726
+ * @return string
2727
+ */
2728
+ public static function get_default_provider() {
2729
+ return static::$default_provider;
2730
+ }
2731
+
2732
  /**
2733
  * Returns the Provider's default region slug.
2734
  *
2828
  return $region;
2829
  }
2830
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2831
  /**
2832
  * AJAX handler for get_buckets()
2833
  */
3528
  /**
3529
  * Apply ACL to an attachment and associated files
3530
  *
3531
+ * @param int $post_id
3532
+ * @param Media_Library_Item $as3cf_item
3533
+ * @param bool $private
3534
  *
3535
+ * @return Media_Library_Item|bool|WP_Error
 
3536
  */
3537
+ public function set_attachment_acl_on_provider( $post_id, Media_Library_Item $as3cf_item, $private ) {
3538
  // Return early if already set to the desired ACL
3539
+ if ( $as3cf_item->is_private() === $private ) {
3540
  return false;
3541
  }
3542
 
3543
+ $acl = $private ? $this->get_provider()->get_private_acl() : $this->get_provider()->get_default_acl();
3544
+
3545
  $args = array(
3546
  'ACL' => $acl,
3547
+ 'Bucket' => $as3cf_item->bucket(),
3548
+ 'Key' => $as3cf_item->path(),
3549
  );
3550
 
3551
+ $region = empty( $as3cf_item->region() ) ? false : $as3cf_item->region();
 
3552
 
3553
  try {
3554
+ $provider_client = $this->get_provider_client( $region, true );
3555
  $provider_client->update_object_acl( $args );
 
 
 
 
 
 
3556
 
3557
+ $as3cf_item = new Media_Library_Item(
3558
+ $as3cf_item->provider(),
3559
+ $as3cf_item->region(),
3560
+ $as3cf_item->bucket(),
3561
+ $as3cf_item->path(),
3562
+ $private,
3563
+ $as3cf_item->source_id(),
3564
+ $as3cf_item->source_path(),
3565
+ wp_basename( $as3cf_item->original_source_path() ),
3566
+ $as3cf_item->private_sizes(),
3567
+ $as3cf_item->id()
3568
+ );
3569
+ $as3cf_item->save();
3570
  } catch ( Exception $e ) {
3571
+ $msg = 'Error setting ACL to ' . $acl . ' for ' . $as3cf_item->path() . ': ' . $e->getMessage();
3572
  AS3CF_Error::log( $msg );
3573
 
3574
  return new WP_Error( 'acl_exception', $msg );
3575
  }
3576
 
3577
+ return $as3cf_item;
3578
  }
3579
 
3580
  /**
3581
  * Make admin notice for when object ACL has changed
3582
  *
3583
+ * @param Media_Library_Item $as3cf_item
3584
  */
3585
+ function make_acl_admin_notice( Media_Library_Item $as3cf_item ) {
3586
+ $filename = wp_basename( $as3cf_item->path() );
3587
+ $acl = $as3cf_item->is_private() ? $this->get_provider()->get_private_acl() : $this->get_provider()->get_default_acl();
3588
  $acl_name = $this->get_acl_display_name( $acl );
3589
  $text = sprintf( __( '<strong>WP Offload Media</strong> &mdash; The file %s has been given %s permissions in the bucket.', 'amazon-s3-and-cloudfront' ), "<strong>{$filename}</strong>", "<strong>{$acl_name}</strong>" );
3590
 
3855
  $media_counts = $this->media_counts();
3856
 
3857
  $output .= 'Media Files: ';
3858
+ $output .= number_format_i18n( $media_counts['total'] ) . ' (paths ' . number_format_i18n( $media_counts['total_paths'] ) . ')';
3859
  $output .= "\r\n";
3860
 
3861
  $output .= 'Offloaded Media Files: ';
3862
+ $output .= number_format_i18n( $media_counts['offloaded'] ) . ' (paths ' . number_format_i18n( $media_counts['offloaded_paths'] ) . ')';
3863
  $output .= "\r\n";
3864
 
3865
+ $output .= 'Not Offloaded Media Files: ';
3866
+ $output .= number_format_i18n( $media_counts['not_offloaded'] ) . ' (paths ' . number_format_i18n( $media_counts['not_offloaded_paths'] ) . ')';
3867
+ $output .= "\r\n";
3868
+ $output .= 'Note: Approximate values, paths *try* and discard duplicates.';
3869
+ $output .= "\r\n\r\n";
3870
+
3871
  $output .= 'Number of Image Sizes: ';
3872
  $sizes = count( get_intermediate_image_sizes() );
3873
  $output .= number_format_i18n( $sizes );
4417
  return intval( $memory_limit ) * 1024 * 1024;
4418
  }
4419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4420
  /**
4421
  * Get the total attachment and total offloaded/not offloaded attachment counts
4422
  *
4427
  */
4428
  public function media_counts( $skip_transient = false, $force = false ) {
4429
  if ( $skip_transient || false === ( $attachment_counts = get_site_transient( 'as3cf_attachment_counts' ) ) ) {
4430
+ $table_prefixes = $this->get_all_blog_table_prefixes();
4431
+ $total = 0;
4432
+ $total_paths = 0;
4433
+ $offloaded = 0;
4434
+ $offloaded_paths = 0;
4435
+ $not_offloaded = 0;
4436
+ $not_offloaded_paths = 0;
4437
 
4438
  foreach ( $table_prefixes as $blog_id => $table_prefix ) {
4439
+ $this->switch_to_blog( $blog_id );
4440
+
4441
+ $counts = Media_Library_Item::count_attachments( $skip_transient, $force );
4442
+ $total += $counts['total'];
4443
+ $total_paths += $counts['total_paths'];
4444
+ $offloaded += $counts['offloaded'];
4445
+ $offloaded_paths += $counts['offloaded_paths'];
4446
+ $not_offloaded += $counts['not_offloaded'];
4447
+ $not_offloaded_paths += $counts['not_offloaded_paths'];
4448
+
4449
+ $this->restore_current_blog();
4450
  }
4451
 
4452
  $attachment_counts = array(
4453
+ 'total' => $total,
4454
+ 'total_paths' => $total_paths,
4455
+ 'offloaded' => $offloaded,
4456
+ 'offloaded_paths' => $offloaded_paths,
4457
+ 'not_offloaded' => $not_offloaded,
4458
+ 'not_offloaded_paths' => $not_offloaded_paths,
4459
  );
4460
 
4461
  set_site_transient( 'as3cf_attachment_counts', $attachment_counts, 2 * MINUTE_IN_SECONDS );
4713
  }
4714
 
4715
  /**
4716
+ * Return a formatted provider info array with display friendly defaults
4717
  *
4718
+ * @param int $id
 
4719
  *
4720
+ * @return bool|array
4721
  */
4722
+ public function get_formatted_provider_info( $id ) {
4723
+ $as3cf_item = Media_Library_Item::get_by_source_id( $id );
4724
+
4725
+ if ( ! $as3cf_item ) {
4726
+ return false;
4727
  }
4728
 
4729
+ $provider_object = $as3cf_item->key_values();
4730
+
4731
+ // Backwards compatibility.
4732
+ $provider_object['key'] = $provider_object['path'];
4733
+ $provider_object['url'] = $this->get_attachment_provider_url( $id, $as3cf_item );
4734
 
4735
+ $acl = $as3cf_item->is_private() ? $this->get_provider()->get_private_acl() : $this->get_provider()->get_default_acl();
4736
  $acl_info = array(
4737
  'acl' => $acl,
4738
  'name' => $this->get_acl_display_name( $acl ),
4739
  'title' => $this->get_media_action_strings( 'change_to_private' ),
4740
  );
4741
 
4742
+ if ( $as3cf_item->is_private() ) {
4743
  $acl_info['title'] = $this->get_media_action_strings( 'change_to_public' );
4744
  }
4745
 
4746
+ $provider_object['acl'] = $acl_info;
4747
+ $provider_object['region'] = $this->get_provider()->get_region_name( $provider_object['region'] );
4748
+ $provider_object['provider_name'] = $this->get_provider_service_name( $provider_object['provider'] );
 
 
 
 
 
 
4749
 
4750
  return $provider_object;
4751
  }
4859
  return reset( $parts );
4860
  }
4861
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4862
  /**
4863
  * Has the given attachment been uploaded by this instance?
4864
  *
4921
  * @return string
4922
  */
4923
  public function get_acl_for_intermediate_size( $attachment_id, $size ) {
4924
+ $as3cf_item = Media_Library_Item::get_by_source_id( $attachment_id );
 
 
 
 
4925
 
4926
+ if ( ! empty( $as3cf_item ) ) {
4927
+ return $as3cf_item->is_private_size( $size ) ? $this->get_provider()->get_private_acl() : $this->get_provider()->get_default_acl();
4928
  }
4929
 
4930
  return $this->get_provider()->get_default_acl();
classes/as3cf-filter.php CHANGED
@@ -330,6 +330,11 @@ abstract class AS3CF_Filter {
330
  $attachment_ids = array();
331
 
332
  foreach ( $matches as $image ) {
 
 
 
 
 
333
  if ( ! preg_match( '/src=\\\?["\']+([^"\'\\\]+)/', $image, $src ) || ! isset( $src[1] ) ) {
334
  // Can't determine URL, skip
335
  continue;
@@ -344,11 +349,6 @@ abstract class AS3CF_Filter {
344
 
345
  $url = AS3CF_Utils::reduce_url( $url );
346
 
347
- if ( ! preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) || ! isset( $class_id[1] ) ) {
348
- // Can't determine ID from class, skip
349
- continue;
350
- }
351
-
352
  $attachment_ids[ $url ] = absint( $class_id[1] );
353
  }
354
 
330
  $attachment_ids = array();
331
 
332
  foreach ( $matches as $image ) {
333
+ if ( ! preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) || ! isset( $class_id[1] ) ) {
334
+ // Can't determine ID from class, skip
335
+ continue;
336
+ }
337
+
338
  if ( ! preg_match( '/src=\\\?["\']+([^"\'\\\]+)/', $image, $src ) || ! isset( $src[1] ) ) {
339
  // Can't determine URL, skip
340
  continue;
349
 
350
  $url = AS3CF_Utils::reduce_url( $url );
351
 
 
 
 
 
 
352
  $attachment_ids[ $url ] = absint( $class_id[1] );
353
  }
354
 
classes/as3cf-notices.php CHANGED
@@ -351,7 +351,7 @@ class AS3CF_Notices {
351
  */
352
  protected function maybe_show_notice( $notice, $dismissed_notices, $tab ) {
353
  $screen = get_current_screen();
354
- if ( $notice['only_show_in_settings'] && false === strpos( $screen->id, $this->as3cf->hook_suffix ) ) {
355
  return;
356
  }
357
 
351
  */
352
  protected function maybe_show_notice( $notice, $dismissed_notices, $tab ) {
353
  $screen = get_current_screen();
354
+ if ( $notice['only_show_in_settings'] && false === strpos( strval( $screen->id ), $this->as3cf->hook_suffix ) ) {
355
  return;
356
  }
357
 
classes/as3cf-plugin-compatibility.php CHANGED
@@ -9,6 +9,7 @@
9
  * @since 0.8.3
10
  */
11
 
 
12
  use DeliciousBrains\WP_Offload_Media\Providers\Provider;
13
 
14
  // Exit if accessed directly
@@ -129,21 +130,21 @@ class AS3CF_Plugin_Compatibility {
129
  * Allow any process to trigger the copy back to local with
130
  * the filter 'as3cf_get_attached_file_copy_back_to_local'
131
  *
132
- * @param string $url
133
- * @param string $file
134
- * @param int $attachment_id
135
- * @param array $provider_object
136
  *
137
  * @return string
138
  */
139
- function legacy_copy_back_to_local( $url, $file, $attachment_id, $provider_object ) {
140
- $copy_back_to_local = apply_filters( 'as3cf_get_attached_file_copy_back_to_local', false, $file, $attachment_id, $provider_object );
141
  if ( false === $copy_back_to_local ) {
142
  // Not copying back file
143
  return $url;
144
  }
145
 
146
- if ( ( $file = $this->copy_provider_file_to_server( $provider_object, $file ) ) ) {
147
  // Return the file if successfully downloaded from S3
148
  return $file;
149
  };
@@ -189,14 +190,14 @@ class AS3CF_Plugin_Compatibility {
189
  /**
190
  * Prevent subsequent attempts to copy back after upload and remove.
191
  *
192
- * @param bool $copy_back_to_local
193
- * @param string $file
194
- * @param integer $attachment_id
195
- * @param array $provider_object
196
  *
197
  * @return bool
198
  */
199
- public function prevent_copy_back_to_local_after_remove( $copy_back_to_local, $file, $attachment_id, $provider_object ) {
200
  if ( $copy_back_to_local && in_array( $file, $this->removed_files ) ) {
201
  $copy_back_to_local = false;
202
  }
@@ -261,20 +262,20 @@ class AS3CF_Plugin_Compatibility {
261
  /**
262
  * Generic method for copying back an S3 file to the server on a specific AJAX action
263
  *
264
- * @param string $action_key Action that must be in process
265
- * @param bool $ajax Must the process be an AJAX one?
266
- * @param string $url S3 URL
267
- * @param string $file Local file path of image
268
- * @param array $provider_object S3 meta data
269
  *
270
  * @return string
271
  */
272
- function copy_image_to_server_on_action( $action_key, $ajax, $url, $file, $provider_object ) {
273
  if ( false === $this->maybe_process_on_action( $action_key, $ajax ) ) {
274
  return $url;
275
  }
276
 
277
- if ( ( $file = $this->copy_provider_file_to_server( $provider_object, $file ) ) ) {
278
  // Return the file if successfully downloaded from S3
279
  return $file;
280
  };
@@ -320,14 +321,33 @@ class AS3CF_Plugin_Compatibility {
320
  return $pre;
321
  }
322
 
323
- $provider_object = $this->as3cf->get_attachment_provider_info( $post_id );
324
- $this->remove_edited_image_files( $post_id, $provider_object );
 
 
 
 
 
325
 
326
  // Update object key with original filename
327
- $restored_filename = wp_basename( $data['file'] );
328
- $old_filename = wp_basename( $provider_object['key'] );
329
- $provider_object['key'] = str_replace( $old_filename, $restored_filename, $provider_object['key'] );
330
- update_post_meta( $post_id, 'amazonS3_info', $provider_object );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
  return true;
333
  }
@@ -335,55 +355,63 @@ class AS3CF_Plugin_Compatibility {
335
  /**
336
  * Remove edited image files from S3.
337
  *
338
- * @param int $attachment_id
339
- * @param array $provider_object
340
  */
341
- protected function remove_edited_image_files( $attachment_id, $provider_object ) {
342
- $bucket = $provider_object['bucket'];
343
- $region = $this->as3cf->get_provider_object_region( $provider_object );
344
- $keys = AS3CF_Utils::get_attachment_edited_keys( $attachment_id, $provider_object );
345
 
346
  if ( empty( $keys ) ) {
347
  return;
348
  }
349
 
350
- $this->as3cf->delete_objects( $region, $bucket, $keys );
351
  }
352
 
353
  /**
354
  * Allow the WordPress Image Editor to edit files that have been copied to S3
355
  * but removed from the local server, by copying them back temporarily
356
  *
357
- * @param string $url
358
- * @param string $file
359
- * @param int $attachment_id
360
- * @param array $provider_object
361
  *
362
  * @return string
363
  */
364
- function image_editor_download_file( $url, $file, $attachment_id, $provider_object ) {
365
  if ( ! $this->is_ajax() ) {
366
  return $url;
367
  }
368
 
369
  // When the image-editor restores the original it requests the edited image,
370
  // but we actually need to copy back the original image at this point
371
- // for the restore to be successful and edited images to be deleted from S3
372
  // via image_editor_remove_files()
373
  if ( isset( $_POST['do'] ) && 'restore' == $_POST['do'] ) {
374
  $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
375
  $original_filename = $backup_sizes['full-orig']['file'];
376
 
377
- $orig_provider = $provider_object;
378
- $orig_provider['key'] = dirname( $provider_object['key'] ) . '/' . $original_filename;
379
- $orig_file = dirname( $file ) . '/' . $original_filename;
 
 
 
 
 
 
 
 
 
 
380
 
381
  // Copy the original file back to the server for the restore process
382
- $this->copy_provider_file_to_server( $orig_provider, $orig_file );
383
 
384
  // Copy the edited file back to the server as well, it will be cleaned up later
385
- if ( $provider_file = $this->copy_provider_file_to_server( $provider_object, $file ) ) {
386
- // Return the file if successfully downloaded from S3
387
  return $provider_file;
388
  };
389
  }
@@ -394,8 +422,8 @@ class AS3CF_Plugin_Compatibility {
394
  foreach ( debug_backtrace() as $caller ) {
395
  if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
396
  // check this has been called by '_load_image_to_edit_path' so as only to copy back once
397
- if ( $provider_file = $this->copy_provider_file_to_server( $provider_object, $file ) ) {
398
- // Return the file if successfully downloaded from S3
399
  return $provider_file;
400
  };
401
  }
@@ -435,7 +463,7 @@ class AS3CF_Plugin_Compatibility {
435
  *
436
  * @return bool
437
  */
438
- protected function is_customizer_crop_action() {
439
  $header_crop = $this->maybe_process_on_action( 'custom-header-crop', true );
440
 
441
  $context = array( 'site-icon', 'custom_logo' );
@@ -450,17 +478,17 @@ class AS3CF_Plugin_Compatibility {
450
  }
451
 
452
  /**
453
- * Allow the WordPress Customizer to crop images that have been copied to S3
454
- * but removed from the local server, by copying them back temporarily
455
  *
456
- * @param string $url
457
- * @param string $file
458
- * @param int $attachment_id
459
- * @param array $provider_object
460
  *
461
  * @return string
462
  */
463
- public function customizer_crop_download_file( $url, $file, $attachment_id, $provider_object ) {
464
  if ( false === $this->is_customizer_crop_action() ) {
465
  return $url;
466
  }
@@ -469,8 +497,8 @@ class AS3CF_Plugin_Compatibility {
469
  return $url;
470
  }
471
 
472
- if ( ( $file = $this->copy_provider_file_to_server( $provider_object, $file ) ) ) {
473
- // Return the file if successfully downloaded from S3
474
  return $file;
475
  };
476
 
@@ -512,84 +540,69 @@ class AS3CF_Plugin_Compatibility {
512
  if ( ! is_null( $post_id ) ) {
513
  return $post_id;
514
  }
515
- $url = parse_url( $url );
516
 
517
- if ( ! isset( $url['path'] ) ) {
518
- return $post_id; // URL path can't be determined
519
- }
520
-
521
- $key1 = ltrim( $url['path'], '/' );
522
- $length1 = strlen( $key1 );
523
-
524
- // URLs may contain the bucket name within the path, therefore we must
525
- // also perform the search with the first path segment removed
526
- $parts = explode( '/', $key1 );
527
- unset( $parts[0] );
528
-
529
- $key2 = implode( '/', $parts );
530
- $length2 = strlen( $key2 );
531
 
532
- global $wpdb;
533
- $sql = $wpdb->prepare( "
534
- SELECT `post_id`
535
- FROM `{$wpdb->prefix}postmeta`
536
- WHERE `{$wpdb->prefix}postmeta`.`meta_key` = 'amazonS3_info'
537
- AND ( `{$wpdb->prefix}postmeta`.`meta_value` LIKE %s
538
- OR `{$wpdb->prefix}postmeta`.`meta_value` LIKE %s )
539
- ",
540
- "%s:3:\"key\";s:{$length1}:\"{$key1}\";%",
541
- "%s:3:\"key\";s:{$length2}:\"{$key2}\";%"
542
- );
543
-
544
- if ( $id = $wpdb->get_var( $sql ) ) {
545
- return $id;
546
  }
547
 
548
- return $post_id; // No attachment found on S3
 
 
 
 
 
549
  }
550
 
551
  /**
552
- * Allow the Regenerate Thumbnails plugin to copy the S3 file back to the local
553
- * server when the file is missing on the server via get_attached_file
554
  *
555
- * @param string $url
556
- * @param string $file
557
- * @param int $attachment_id
558
- * @param array $provider_object
559
  *
560
  * @return string
561
  */
562
- function regenerate_thumbnails_download_file( $url, $file, $attachment_id, $provider_object ) {
563
- return $this->copy_image_to_server_on_action( 'regeneratethumbnail', true, $url, $file, $provider_object );
564
  }
565
 
566
  /**
567
- * Download a file from S3 if the file does not exist locally and places it where
568
  * the attachment's file should be.
569
  *
570
- * @param array $provider_object
571
- * @param string $file
572
  *
573
  * @return string|bool File if downloaded, false on failure
574
  */
575
- public function copy_provider_file_to_server( $provider_object, $file ) {
576
  // Make sure the directory exists
577
  $dir = dirname( $file );
578
  if ( ! wp_mkdir_p( $dir ) ) {
579
  $error_message = sprintf( __( 'The local directory %s does not exist and could not be created.', 'amazon-s3-and-cloudfront' ), $dir );
580
- AS3CF_Error::log( sprintf( __( 'There was an error attempting to download the file %s from the bucket: %s', 'amazon-s3-and-cloudfront' ), $provider_object['key'], $error_message ) );
581
 
582
  return false;
583
  }
584
 
585
  try {
586
- $this->as3cf->get_provider_client( $provider_object['region'], true )->get_object( array(
587
- 'Bucket' => $provider_object['bucket'],
588
- 'Key' => $provider_object['key'],
589
  'SaveAs' => $file,
590
  ) );
591
  } catch ( Exception $e ) {
592
- AS3CF_Error::log( sprintf( __( 'There was an error attempting to download the file %s from the bucket: %s', 'amazon-s3-and-cloudfront' ), $provider_object['key'], $e->getMessage() ) );
593
 
594
  return false;
595
  }
@@ -627,25 +640,25 @@ class AS3CF_Plugin_Compatibility {
627
  * Allow access to the remote file via the stream wrapper.
628
  * This is useful for compatibility with plugins when attachments are removed from the local server after upload.
629
  *
630
- * @param string $url
631
- * @param string $file
632
- * @param int $attachment_id
633
- * @param array $provider_object
634
  *
635
  * @return string
636
  * @throws Exception
637
  */
638
- public function get_stream_wrapper_file( $url, $file, $attachment_id, $provider_object ) {
639
  if ( $url === $file ) {
640
  // Abort if an earlier hook to get the file has been called and it has been copied back.
641
  return $file;
642
  }
643
 
644
  // Make sure the region stream wrapper is registered.
645
- $client = $this->register_stream_wrapper( $provider_object['region'] );
646
 
647
  if ( ! empty( $client ) ) {
648
- return $client->prepare_stream_wrapper_file( $provider_object['region'], $provider_object['bucket'], $provider_object['key'] );
649
  }
650
 
651
  return $url;
@@ -775,7 +788,7 @@ class AS3CF_Plugin_Compatibility {
775
  }
776
 
777
  /**
778
- * Alter the image meta data to add srcset support for object versioned S3 URLs
779
  *
780
  * @param array $image_meta
781
  * @param array $size_array
@@ -795,14 +808,14 @@ class AS3CF_Plugin_Compatibility {
795
  return $image_meta;
796
  }
797
 
798
- if ( ! ( $provider_object = $this->as3cf->is_attachment_served_by_provider( $attachment_id ) ) ) {
799
  // Attachment not uploaded to S3, abort
800
  return $image_meta;
801
  }
802
 
803
  $image_basename = $this->as3cf->encode_filename_in_path( wp_basename( $image_meta['file'] ) );
804
 
805
- if ( false === strpos( $this->as3cf->encode_filename_in_path( $provider_object['key'] ), $image_basename ) ) {
806
  // Not the correct attachment, abort
807
  return $image_meta;
808
  }
@@ -825,7 +838,7 @@ class AS3CF_Plugin_Compatibility {
825
  }
826
 
827
  /**
828
- * Replace local URLs with S3 ones for srcset image sources
829
  *
830
  * @param array $sources
831
  * @param array $size_array
@@ -841,7 +854,7 @@ class AS3CF_Plugin_Compatibility {
841
  return $sources;
842
  }
843
 
844
- if ( ! ( $provider_object = $this->as3cf->is_attachment_served_by_provider( $attachment_id ) ) ) {
845
  // Attachment not uploaded to S3, abort
846
  return $sources;
847
  }
@@ -849,7 +862,7 @@ class AS3CF_Plugin_Compatibility {
849
  foreach ( $sources as $width => $source ) {
850
  $filename = wp_basename( $source['url'] );
851
  $size = $this->find_image_size_from_width( $image_meta['sizes'], $width, $filename );
852
- $provider_url = $this->as3cf->get_attachment_provider_url( $attachment_id, $provider_object, null, $size, $image_meta );
853
 
854
  if ( false === $provider_url || is_wp_error( $provider_url ) ) {
855
  // Skip URLs not offloaded to S3
9
  * @since 0.8.3
10
  */
11
 
12
+ use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
13
  use DeliciousBrains\WP_Offload_Media\Providers\Provider;
14
 
15
  // Exit if accessed directly
130
  * Allow any process to trigger the copy back to local with
131
  * the filter 'as3cf_get_attached_file_copy_back_to_local'
132
  *
133
+ * @param string $url
134
+ * @param string $file
135
+ * @param int $attachment_id
136
+ * @param Media_Library_Item $as3cf_item
137
  *
138
  * @return string
139
  */
140
+ function legacy_copy_back_to_local( $url, $file, $attachment_id, Media_Library_Item $as3cf_item ) {
141
+ $copy_back_to_local = apply_filters( 'as3cf_get_attached_file_copy_back_to_local', false, $file, $attachment_id, $as3cf_item );
142
  if ( false === $copy_back_to_local ) {
143
  // Not copying back file
144
  return $url;
145
  }
146
 
147
+ if ( ( $file = $this->copy_provider_file_to_server( $as3cf_item, $file ) ) ) {
148
  // Return the file if successfully downloaded from S3
149
  return $file;
150