diff --git a/README.md b/README.md index 21236021..80c020d2 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ **Contributors:** bradt, deliciousbrains **Tags:** uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront **Requires at least:** 4.4 -**Tested up to:** 4.7.1 -**Stable tag:** 1.1.5 +**Tested up to:** 4.7.3 +**Stable tag:** 1.1.6 **License:** GPLv3 Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery. @@ -69,6 +69,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin ## Changelog ## +### WP Offload S3 Lite 1.1.6 - 2017-03-13 ### +* New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/) +* New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content +* Improvement: Ensure files uploaded using `media_handle_sideload` have unique filename on S3 when 'Remove Files From Server' enabled +* Bug fix: Files uploaded to S3 with empty filenames when the filename started with non-latin characters +* Bug fix: Audio files with private ACL not working with WordPress's default media player +* Bug fix: S3 API version not passed to S3 client +* Bug fix: Content added to text widgets via the Customizer not saved +* Bug fix: Original file not removed locally when cropped via the Customizer and 'Remove Files From Server' enabled +* Bug fix: Incorrect Media Library URLs saved to the database when WordPress installed in a subdirectory + ### WP Offload S3 Lite 1.1.5 - 2017-01-12 ### * Improvement: Filter custom CSS - S3 URLs will no longer be saved to the database * Bug fix: PDF previews have incorrect MIME type diff --git a/assets/js/media.js b/assets/js/media.js index c087b93b..c9912484 100644 --- a/assets/js/media.js +++ b/assets/js/media.js @@ -140,12 +140,15 @@ var test = {}; }, updateACL: function( response ) { - if ( 'undefined' === typeof response.acl_display || 'undefined' === typeof response.title || 'undefined' === typeof response.acl ) { + if ( null == response.acl_display || null == response.title || null == response.acl || null == response.url ) { this.renderACLError(); return; } + this.model.set( 'url', response.url ); + this.render(); + var toggle = $( '#as3cfpro-toggle-acl' ); $( '#as3cfpro-updating' ).remove(); diff --git a/assets/js/media.min.js b/assets/js/media.min.js index 9d9e2176..286bc5ba 100644 --- a/assets/js/media.min.js +++ b/assets/js/media.min.js @@ -1 +1 @@ -var test={};!function(a,b){var c=wp.media,d=c.view.Attachment.Details.TwoColumn;c.view.Attachment.Details.TwoColumn=d.extend({events:function(){return b.extend({},d.prototype.events,{"click .local-warning":"confirmS3Removal","click #as3cfpro-toggle-acl":"toggleACL"})},render:function(){this.fetchS3Details(this.model.get("id"))},fetchS3Details:function(a){wp.ajax.send("as3cf_get_attachment_s3_details",{data:{_nonce:as3cf_media.nonces.get_attachment_s3_details,id:a}}).done(b.bind(this.renderView,this))},renderView:function(a){d.prototype.render.apply(this),this.renderActionLinks(a),this.renderS3Details(a)},renderActionLinks:function(c){var d=c&&c.links||[],e=this.$el.find(".actions"),f=a("
",{"class":"s3-actions"}),g=[];b(d).each(function(a){g.push(a)}),f.append(g.join(" | ")),e.append(f)},renderS3Details:function(a){if(a&&a.s3object){var b=this.$el.find(".attachment-info .details"),c=this.generateDetails(a,["bucket","key","region","acl"]);b.append(c)}},generateDetails:function(a,c){var d="",e=b.template('
<%= label %>: <%= value %>
');return b(c).each(function(c){if(a.s3object[c]){var f=a.s3object[c];if("acl"===c&&(f=a.s3object[c].name,a.acl_toggle)){var g=b.template('<%= value %>');f=g({title:a.s3object[c].title,acl:a.s3object[c].acl,value:f})}d+=e({key:c,label:as3cf_media.strings[c],value:f})}}),d},confirmS3Removal:function(a){if(!confirm(as3cfpro_media.strings.local_warning))return a.preventDefault(),a.stopImmediatePropagation(),!1},toggleACL:function(c){c.preventDefault();var d=a("#as3cfpro-toggle-acl"),e=d.attr("data-currentACL"),f=as3cfpro_media.settings.private_acl;d.hide(),d.after(''+as3cfpro_media.strings.updating_acl+""),e===as3cfpro_media.settings.private_acl&&(f=as3cfpro_media.settings.default_acl),wp.ajax.send("as3cfpro_update_acl",{data:{_nonce:as3cfpro_media.nonces.update_acl,id:this.model.get("id"),acl:f}}).done(b.bind(this.updateACL,this)).fail(b.bind(this.renderACLError,this))},renderACLError:function(){a("#as3cfpro-updating").remove(),a("#as3cfpro-toggle-acl").show(),alert(as3cfpro_media.strings.change_acl_error)},updateACL:function(b){if("undefined"==typeof b.acl_display||"undefined"==typeof b.title||"undefined"==typeof b.acl)return void this.renderACLError();var c=a("#as3cfpro-toggle-acl");a("#as3cfpro-updating").remove(),c.text(b.acl_display),c.attr("title",b.title),c.attr("data-currentACL",b.acl),c.show()}})}(jQuery,_); \ No newline at end of file +var test={};!function(a,b){var c=wp.media,d=c.view.Attachment.Details.TwoColumn;c.view.Attachment.Details.TwoColumn=d.extend({events:function(){return b.extend({},d.prototype.events,{"click .local-warning":"confirmS3Removal","click #as3cfpro-toggle-acl":"toggleACL"})},render:function(){this.fetchS3Details(this.model.get("id"))},fetchS3Details:function(a){wp.ajax.send("as3cf_get_attachment_s3_details",{data:{_nonce:as3cf_media.nonces.get_attachment_s3_details,id:a}}).done(b.bind(this.renderView,this))},renderView:function(a){d.prototype.render.apply(this),this.renderActionLinks(a),this.renderS3Details(a)},renderActionLinks:function(c){var d=c&&c.links||[],e=this.$el.find(".actions"),f=a("
",{"class":"s3-actions"}),g=[];b(d).each(function(a){g.push(a)}),f.append(g.join(" | ")),e.append(f)},renderS3Details:function(a){if(a&&a.s3object){var b=this.$el.find(".attachment-info .details"),c=this.generateDetails(a,["bucket","key","region","acl"]);b.append(c)}},generateDetails:function(a,c){var d="",e=b.template('
<%= label %>: <%= value %>
');return b(c).each(function(c){if(a.s3object[c]){var f=a.s3object[c];if("acl"===c&&(f=a.s3object[c].name,a.acl_toggle)){var g=b.template('<%= value %>');f=g({title:a.s3object[c].title,acl:a.s3object[c].acl,value:f})}d+=e({key:c,label:as3cf_media.strings[c],value:f})}}),d},confirmS3Removal:function(a){if(!confirm(as3cfpro_media.strings.local_warning))return a.preventDefault(),a.stopImmediatePropagation(),!1},toggleACL:function(c){c.preventDefault();var d=a("#as3cfpro-toggle-acl"),e=d.attr("data-currentACL"),f=as3cfpro_media.settings.private_acl;d.hide(),d.after(''+as3cfpro_media.strings.updating_acl+""),e===as3cfpro_media.settings.private_acl&&(f=as3cfpro_media.settings.default_acl),wp.ajax.send("as3cfpro_update_acl",{data:{_nonce:as3cfpro_media.nonces.update_acl,id:this.model.get("id"),acl:f}}).done(b.bind(this.updateACL,this)).fail(b.bind(this.renderACLError,this))},renderACLError:function(){a("#as3cfpro-updating").remove(),a("#as3cfpro-toggle-acl").show(),alert(as3cfpro_media.strings.change_acl_error)},updateACL:function(b){if(null==b.acl_display||null==b.title||null==b.acl||null==b.url)return void this.renderACLError();this.model.set("url",b.url),this.render();var c=a("#as3cfpro-toggle-acl");a("#as3cfpro-updating").remove(),c.text(b.acl_display),c.attr("title",b.title),c.attr("data-currentACL",b.acl),c.show()}})}(jQuery,_); \ No newline at end of file diff --git a/assets/js/modal.js b/assets/js/modal.js index ac9f9d11..bb863392 100644 --- a/assets/js/modal.js +++ b/assets/js/modal.js @@ -2,7 +2,8 @@ var as3cfModal = (function( $ ) { var modal = { prefix: 'as3cf', - loading: false + loading: false, + dismissible: true }; var modals = {}; @@ -54,7 +55,12 @@ var as3cfModal = (function( $ ) { var $overlay = $( '#as3cf-overlay' ); // Modal container - $overlay.append( '
×
' ); + if ( modal.dismissible ) { + $overlay.append( '
×
' ); + } else { + $overlay.append( '
' ); + } + var $modal = $( '#as3cf-modal' ); if ( undefined === modals[ key ] ) { @@ -88,7 +94,7 @@ var as3cfModal = (function( $ ) { * @param {function} callback */ modal.close = function( callback ) { - if ( modal.loading ) { + if ( modal.loading || ! modal.dismissible ) { return; } @@ -110,12 +116,21 @@ var as3cfModal = (function( $ ) { /** * Set loading state * - * @param {bool} state + * @param {boolean} state */ modal.setLoadingState = function( state ) { modal.loading = state; }; + /** + * Set dismissible state. + * + * @param {boolean} state + */ + modal.setDismissibleState = function( state ) { + modal.dismissible = state; + }; + // Setup click handlers $( document ).ready( function() { diff --git a/assets/js/modal.min.js b/assets/js/modal.min.js index feac1c9e..bd5a79a7 100644 --- a/assets/js/modal.min.js +++ b/assets/js/modal.min.js @@ -1 +1 @@ -var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf",loading:!1},d={};return c.exists=function(c){var e=b(c);return void 0!==d[e]||!!a(c).length},c.open=function(c,e,f){var g=b(c);a("body").append('
');var h=a("#as3cf-overlay");h.append('
×
');var i=a("#as3cf-modal");if(void 0===d[g]){var j=a(c);d[g]=j.clone(!0).css("display","block"),j.remove()}i.data("as3cf-modal-target",c).append(d[g]),void 0!==f&&i.addClass(f),"function"==typeof e&&e(c),a("body").addClass("as3cf-modal-open"),h.fadeIn(150),i.fadeIn(150),a("body").trigger("as3cf-modal-open",[c])},c.close=function(b){if(!c.loading){var d=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){"function"==typeof b&&b(d),a("body").removeClass("as3cf-modal-open"),a(this).remove()}),a("body").trigger("as3cf-modal-close",[d])}},c.setLoadingState=function(a){c.loading=a},a(document).ready(function(){a("body").on("click","[data-as3cf-modal]",function(b){b.preventDefault(),c.open(a(this).data("as3cf-modal")+"."+c.prefix)}),a("body").on("click","#as3cf-overlay, .close-as3cf-modal",function(a){return a.preventDefault(),a.target===this&&void c.close()})}),c}(jQuery); \ No newline at end of file +var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf",loading:!1,dismissible:!0},d={};return c.exists=function(c){var e=b(c);return void 0!==d[e]||!!a(c).length},c.open=function(e,f,g){var h=b(e);a("body").append('
');var i=a("#as3cf-overlay");c.dismissible?i.append('
×
'):i.append('
');var j=a("#as3cf-modal");if(void 0===d[h]){var k=a(e);d[h]=k.clone(!0).css("display","block"),k.remove()}j.data("as3cf-modal-target",e).append(d[h]),void 0!==g&&j.addClass(g),"function"==typeof f&&f(e),a("body").addClass("as3cf-modal-open"),i.fadeIn(150),j.fadeIn(150),a("body").trigger("as3cf-modal-open",[e])},c.close=function(b){if(!c.loading&&c.dismissible){var d=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){"function"==typeof b&&b(d),a("body").removeClass("as3cf-modal-open"),a(this).remove()}),a("body").trigger("as3cf-modal-close",[d])}},c.setLoadingState=function(a){c.loading=a},c.setDismissibleState=function(a){c.dismissible=a},a(document).ready(function(){a("body").on("click","[data-as3cf-modal]",function(b){b.preventDefault(),c.open(a(this).data("as3cf-modal")+"."+c.prefix)}),a("body").on("click","#as3cf-overlay, .close-as3cf-modal",function(a){return a.preventDefault(),a.target===this&&void c.close()})}),c}(jQuery); \ No newline at end of file diff --git a/classes/amazon-s3-and-cloudfront.php b/classes/amazon-s3-and-cloudfront.php index 06948d42..1addec66 100644 --- a/classes/amazon-s3-and-cloudfront.php +++ b/classes/amazon-s3-and-cloudfront.php @@ -12,6 +12,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base { */ private $s3client; + /** + * @var array + */ + private $uploaded_post_ids = array(); + /** * @var string */ @@ -76,6 +81,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base { const PRIVATE_ACL = 'private'; const DEFAULT_EXPIRES = 900; const DEFAULT_REGION = 'us-east-1'; + const AWS_SIGNATURE = 'v4'; + const S3_API_VERSION = '2006-03-01'; const SETTINGS_KEY = 'tantan_wordpress_s3'; const SETTINGS_CONSTANT = 'WPOS3_SETTINGS'; @@ -92,7 +99,7 @@ function __construct( $plugin_file_path, $aws, $slug = null ) { parent::__construct( $plugin_file_path ); - $this->aws = $aws; + $this->aws = $aws; $this->notices = AS3CF_Notices::get_instance( $this ); $this->init( $plugin_file_path ); @@ -140,12 +147,14 @@ function init( $plugin_file_path ) { add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 ); add_filter( 'get_image_tag', array( $this, 'maybe_encode_get_image_tag' ), 99, 6 ); add_filter( 'wp_get_attachment_image_src', array( $this, 'maybe_encode_wp_get_attachment_image_src' ), 99, 4 ); - add_filter( 'wp_prepare_attachment_for_js', array( $this, 'maybe_encode_wp_prepare_attachment_for_js' ), 99, 3 ); + add_filter( 'wp_prepare_attachment_for_js', array( $this, 'maybe_encode_wp_prepare_attachment_for_js', ), 99, 3 ); add_filter( 'image_get_intermediate_size', array( $this, 'maybe_encode_image_get_intermediate_size' ), 99, 3 ); add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 ); + add_filter( 'wp_audio_shortcode', array( $this, 'wp_audio_shortcode' ), 100, 5 ); // Communication with S3, plugin needs to be setup add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 ); + add_filter( 'wp_handle_sideload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 ); add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 110, 2 ); add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 ); add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 ); @@ -244,7 +253,10 @@ function get_setting( $key, $default = '' ) { $settings = $this->get_settings(); // If legacy setting set, migrate settings - if ( isset( $settings['wp-uploads'] ) && $settings['wp-uploads'] && in_array( $key, array( 'copy-to-s3', 'serve-from-s3' ) ) ) { + if ( isset( $settings['wp-uploads'] ) && + $settings['wp-uploads'] && + in_array( $key, array( 'copy-to-s3', 'serve-from-s3', ) ) + ) { return '1'; } @@ -662,7 +674,7 @@ protected function get_local_url_preview( $escape = true, $suffix = 'photo.jpg' /** * Generate a preview of the URL of files uploaded to S3 * - * @param bool $escape + * @param bool $escape * @param string $suffix * * @return string @@ -763,7 +775,7 @@ function delete_s3_objects( $region, $bucket, $objects, $log_error = false, $ret * @param bool $force_new_s3_client if we are deleting in bulk, force new S3 client * to cope with possible different regions */ - function remove_attachment_files_from_s3( $post_id, $s3object, $remove_backup_sizes = true, $log_error = false, $return_on_error = false, $force_new_s3_client = false ) { + function remove_attachment_files_from_s3( $post_id, $s3object, $remove_backup_sizes = true, $log_error = false, $return_on_error = false, $force_new_s3_client = false ) { $prefix = $this->normalize_object_prefix( $s3object['key'] ); $bucket = $s3object['bucket']; $region = $this->get_s3object_region( $s3object ); @@ -778,7 +790,7 @@ function remove_attachment_files_from_s3( $post_id, $s3object, $remove_backup_si foreach ( $paths as $path ) { $objects_to_remove[] = array( - 'Key' => $prefix . basename( $path ), + 'Key' => $prefix . wp_basename( $path ), ); } @@ -793,7 +805,7 @@ function remove_attachment_files_from_s3( $post_id, $s3object, $remove_backup_si * @param bool $force_new_s3_client if we are deleting in bulk, force new S3 client * to cope with possible different regions */ - function delete_attachment( $post_id, $force_new_s3_client = false ) { + function delete_attachment( $post_id, $force_new_s3_client = false ) { if ( ! $this->is_plugin_setup() ) { return; } @@ -850,7 +862,7 @@ function wp_update_attachment_metadata( $data, $post_id ) { * * @return array|WP_Error $s3object|$meta If meta is supplied, return it. Else return S3 meta */ - function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $force_new_s3_client = false, $remove_local_files = true ) { + public function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $force_new_s3_client = false, $remove_local_files = true ) { $return_metadata = null; if ( is_null( $data ) ) { $data = wp_get_attachment_metadata( $post_id, true ); @@ -887,7 +899,7 @@ function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $fo return $this->return_upload_error( $error_msg, $return_metadata ); } - $file_name = basename( $file_path ); + $file_name = wp_basename( $file_path ); $type = get_post_mime_type( $post_id ); $allowed_types = $this->get_allowed_mime_types(); @@ -1017,7 +1029,7 @@ function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $fo $acl = apply_filters( 'as3cf_upload_acl_sizes', self::DEFAULT_ACL, $size, $post_id, $data ); $additional_images[] = array( - 'Key' => $prefix . basename( $file_path ), + 'Key' => $prefix . wp_basename( $file_path ), 'SourceFile' => $file_path, 'ACL' => $acl, 'ContentType' => $this->get_mime_type( $file_path ), @@ -1084,6 +1096,9 @@ function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $fo update_post_meta( $post_id, 'amazonS3_info', $s3object ); } + // Keep track of attachments uploaded by this instance. + $this->uploaded_post_ids[] = $post_id; + do_action( 'wpos3_post_upload_attachment', $post_id, $s3object ); if ( ! is_null( $return_metadata ) ) { @@ -1102,7 +1117,7 @@ function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $fo * @return string */ protected function get_mime_type( $file_path ) { - $file_type = wp_check_filetype_and_ext( $file_path, basename( $file_path ) ); + $file_type = wp_check_filetype_and_ext( $file_path, wp_basename( $file_path ) ); return $file_type['type']; } @@ -1195,20 +1210,6 @@ function remove_local_files( $file_paths ) { } } - /** - * Helper to apply a suffix to a file path - * - * @param string $file - * @param string $suffix - * - * @return string - */ - function apply_file_suffix( $file, $suffix ) { - $pathinfo = pathinfo( $file ); - - return $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $suffix . '.' . $pathinfo['extension']; - } - /** * Get the object versioning string prefix * @@ -1279,52 +1280,66 @@ function get_attachment_folder_time( $post_id ) { } /** - * Create unique names for file to be uploaded to AWS - * This only applies when the remove local file option is enabled + * Filter file details before upload. * - * @param array $file An array of data for a single file. + * @param array $file An array of data for a single file. * * @return array $file The altered file array with AWS unique filename. */ - function wp_handle_upload_prefilter( $file ) { + public function wp_handle_upload_prefilter( $file ) { + // Get Post ID if uploaded in post screen. + $post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT ); + + $file['name'] = $this->filter_unique_filename( $file['name'], $post_id ); + + return $file; + } + + /** + * Create unique names for file to be uploaded to AWS. + * This only applies when the remove local file option is enabled. + * + * @param string $filename Unique file name. + * @param int $post_id Attachment's parent Post ID. + * + * @return string + */ + public function filter_unique_filename( $filename, $post_id = null ) { if ( ! $this->get_setting( 'copy-to-s3' ) || ! $this->is_plugin_setup() ) { - return $file; + return $filename; } - $filename = $file['name']; - // sanitize the file name before we begin processing $filename = sanitize_file_name( $filename ); - // separate the filename into a name and extension - $info = pathinfo( $filename ); - $ext = ! empty( $info['extension'] ) ? '.' . $info['extension'] : ''; - $name = basename( $filename, $ext ); + // Get base filename without extension. + $ext = pathinfo( $filename, PATHINFO_EXTENSION ); + $ext = $ext ? ".$ext" : ''; + $name = wp_basename( $filename, $ext ); - // edge case: if file is named '.ext', treat as an empty name + // Edge case: if file is named '.ext', treat as an empty name. if ( $name === $ext ) { $name = ''; } - // rebuild filename with lowercase extension as S3 will have converted extension on upload + // Rebuild filename with lowercase extension as S3 will have converted extension on upload. $ext = strtolower( $ext ); - $filename = $info['filename'] . $ext; + $filename = $name . $ext; $time = current_time( 'mysql' ); - // Get time if uploaded in post screen - $post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT ); - if ( isset( $post_id ) ) { + // Get time if uploaded in post screen. + if ( ! empty( $post_id ) ) { $time = $this->get_post_time( $post_id ); } if ( ! $this->does_file_exist( $filename, $time ) ) { - // File doesn't exist locally or on S3, return it - return $file; + // File doesn't exist locally or on S3, return it. + return $filename; } - $file['name'] = $this->generate_unique_filename( $name, $ext, $time ); + $filename = $this->generate_unique_filename( $name, $ext, $time ); - return $file; + return $filename; } /** @@ -1414,7 +1429,7 @@ function does_file_exist_s3( $filename, $time ) { } $s3client = $this->get_s3client( $region ); - $prefix = ltrim( trailingslashit( $this->get_object_prefix() ), '/' ); + $prefix = ltrim( trailingslashit( $this->get_object_prefix() ), '/' ); $prefix .= ltrim( trailingslashit( $this->get_dynamic_prefix( $time ) ), '/' ); return $s3client->doesObjectExist( $bucket, $prefix . $filename ); @@ -1493,6 +1508,7 @@ function get_secure_attachment_url( $post_id, $expires = null, $size = null, $he if ( is_null( $expires ) ) { $expires = self::DEFAULT_EXPIRES; } + return $this->get_attachment_url( $post_id, $expires, $size, null, $headers, $skip_rewrite_check ); } @@ -1699,16 +1715,7 @@ public function get_attachment_local_url( $post_id ) { return false; } - // Set correct domain on multisite subdomain installs - if ( is_multisite() ) { - $siteurl = trailingslashit( get_option( 'siteurl' ) ); - $network_siteurl = trailingslashit( network_site_url() ); - - if ( 0 !== strpos( $url, $siteurl ) ) { - // URL already using site URL, no replacement needed - $url = str_replace( $network_siteurl, $siteurl, $url ); - } - } + $url = $this->maybe_fix_local_subsite_url( $url ); return $url; } @@ -1796,7 +1803,8 @@ public function get_attachment_s3_url( $post_id, $s3object, $expires = null, $si if ( ! is_null( $expires ) && $this->is_plugin_setup() ) { try { $expires = time() + apply_filters( 'as3cf_expires', $expires ); - $secure_url = $this->get_s3client( $region )->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires, $headers ); + $secure_url = $this->get_s3client( $region ) + ->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires, $headers ); return apply_filters( 'as3cf_get_attachment_secure_url', $secure_url, $s3object, $post_id, $expires, $headers ); } catch ( Exception $e ) { @@ -1992,7 +2000,7 @@ protected function convert_dimensions_to_size_name( $attachment_id, $dimensions } $sizes = $meta['sizes']; - uasort( $sizes, function( $a, $b ) { + uasort( $sizes, function ( $a, $b ) { // Order by image area return ( $a['width'] * $a['height'] ) - ( $b['width'] * $b['height'] ); } ); @@ -2077,9 +2085,9 @@ public function encode_filename_in_path( $file ) { if ( isset( $url['query'] ) ) { // Manually strip query string, as passing $url['path'] to basename results in corrupt � characters - $file_name = basename( str_replace( '?' . $url['query'], '', $file ) ); + $file_name = wp_basename( str_replace( '?' . $url['query'], '', $file ) ); } else { - $file_name = basename( $file ); + $file_name = wp_basename( $file ); } if ( false !== strpos( $file_name, '%' ) ) { @@ -2112,7 +2120,7 @@ public function decode_filename_in_path( $file ) { return $file; } - $file_name = basename( $url['path'] ); + $file_name = wp_basename( $url['path'] ); if ( false === strpos( $file_name, '%' ) ) { // File name not encoded, return original @@ -2128,7 +2136,7 @@ public function decode_filename_in_path( $file ) { * Allow processes to update the file on S3 via update_attached_file() * * @param string $file - * @param int $attachment_id + * @param int $attachment_id * * @return string */ @@ -2175,8 +2183,7 @@ function get_attached_file( $file, $attachment_id ) { * @param array $return */ function end_ajax( $return = array() ) { - echo json_encode( $return ); - exit; + wp_send_json( $return ); } function verify_ajax_request() { @@ -2226,7 +2233,7 @@ function ajax_create_bucket() { $previous_manual_bucket_select = $this->get_setting( 'manual_bucket', false ); $args = array( - '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ) + '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ), ); $this->save_bucket_for_ajax( $bucket, $previous_manual_bucket_select, $region, $args ); @@ -2254,8 +2261,7 @@ function create_bucket( $bucket_name, $region = null ) { } $this->get_s3client()->createBucket( $args ); - } - catch ( Exception $e ) { + } catch ( Exception $e ) { return new WP_Error( 'exception', $e->getMessage() ); } @@ -2395,7 +2401,13 @@ function save_bucket( $bucket_name, $manual = false, $region = null ) { * @param Amazon_Web_Services $aws */ function admin_menu( $aws ) { - $hook_suffix = $aws->add_page( $this->get_plugin_page_title(), $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) ); + $hook_suffix = $aws->add_page( + $this->get_plugin_page_title(), + $this->plugin_menu_title, + 'manage_options', + $this->plugin_slug, + array( $this, 'render_page' ) + ); if ( false !== $hook_suffix ) { $this->hook_suffix = $hook_suffix; @@ -2414,13 +2426,13 @@ function admin_menu( $aws ) { function get_s3client( $region = false, $force = false ) { if ( is_null( $this->s3client ) || $force ) { + $args = array( + 'version' => self::S3_API_VERSION, + ); + if ( $region ) { - $args = array( - 'region' => $this->translate_region( $region ), - 'signature' => 'v4', - ); - } else { - $args = array(); + $args['region'] = $this->translate_region( $region ); + $args['signature'] = self::AWS_SIGNATURE; } $client = $this->aws->get_client()->get( 's3', $args ); @@ -2467,7 +2479,7 @@ function get_bucket_region( $bucket ) { * * * @param array $s3object - * @param int $post_id - if supplied will update the s3 meta if no region found + * @param int $post_id - if supplied will update the s3 meta if no region found * * @return string|WP_Error - region name */ @@ -2542,8 +2554,7 @@ function ajax_get_buckets() { function get_buckets() { try { $result = $this->get_s3client()->listBuckets(); - } - catch ( Exception $e ) { + } catch ( Exception $e ) { return new WP_Error( 'exception', $e->getMessage() ); } @@ -2827,8 +2838,7 @@ function render_page() { if ( is_wp_error( $aws_client ) ) { $this->render_view( 'error-fatal', array( 'message' => $aws_client->get_error_message() ) ); - } - else { + } else { $this->render_view( 'settings-tabs' ); do_action( 'as3cf_pre_settings_render' ); $this->render_view( 'settings' ); @@ -2846,7 +2856,7 @@ function render_page() { function get_settings_tabs() { $tabs = array( 'media' => _x( 'Media Library', 'Show the media library tab', 'amazon-s3-and-cloudfront' ), - 'support' => _x( 'Support', 'Show the support tab', 'amazon-s3-and-cloudfront' ) + 'support' => _x( 'Support', 'Show the support tab', 'amazon-s3-and-cloudfront' ), ); return apply_filters( 'as3cf_settings_tabs', $tabs ); @@ -3060,7 +3070,7 @@ function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) { * @param array $s3object */ function make_acl_admin_notice( $s3object ) { - $filename = basename( $s3object['key'] ); + $filename = wp_basename( $s3object['key'] ); $acl = ( isset( $s3object['acl'] ) ) ? $s3object['acl'] : self::DEFAULT_ACL; $acl_name = $this->get_acl_display_name( $acl ); $text = sprintf( __( 'WP Offload S3 — The file %s has been given %s permissions on Amazon S3.', 'amazon-s3-and-cloudfront' ), "{$filename}", "{$acl_name}" ); @@ -3080,7 +3090,7 @@ function check_for_gd_imagick() { $gd_enabled = $this->gd_enabled(); $imagick_enabled = $this->imagick_enabled(); - if( ! $gd_enabled && ! $imagick_enabled ) { + if ( ! $gd_enabled && ! $imagick_enabled ) { $this->notices->add_notice( __( 'WP Offload S3 Requirement Missing — Looks like you don\'t have an image manipulation library installed on this server and configured with PHP. You may run into trouble if you try to edit images. Please setup GD or ImageMagick.', 'amazon-s3-and-cloudfront' ), array( 'flash' => false, 'only_show_to_user' => false, 'only_show_in_settings' => true ) @@ -3703,7 +3713,12 @@ public function get_attachment_file_paths( $attachment_id, $exists_locally = tru return $paths; } - $file_name = basename( $file_path ); + $file_name = wp_basename( $file_path ); + + // If file edited, current file name might be different. + if ( isset( $meta['file'] ) ) { + $paths['file'] = str_replace( $file_name, wp_basename( $meta['file'] ), $file_path ); + } // Thumb if ( isset( $meta['thumb'] ) ) { @@ -3807,7 +3822,7 @@ function multisite_get_spaced_used( $space_used ) { * @return bool */ public function memory_exceeded( $filter_name = null ) { - $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory + $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory $current_memory = memory_get_usage( true ); $return = false; @@ -3857,7 +3872,7 @@ public function get_memory_limit() { public function count_attachments( $prefix, $uploaded_to_s3 = null ) { global $wpdb; - $sql = "SELECT COUNT(*) + $sql = "SELECT COUNT(DISTINCT p.ID) FROM `{$prefix}posts` p"; $where = "WHERE p.post_type = 'attachment'"; @@ -3931,7 +3946,7 @@ function plugin_deactivated_notice() { * * @param string $code * @param string $message - * @param mixed $data + * @param mixed $data * * @return WP_Error */ @@ -4111,7 +4126,14 @@ public function maybe_update_cloudfront_path( $path ) { * Add the S3 meta box to the attachment screen */ public function attachment_s3_meta_box() { - add_meta_box( 's3-actions', __( 'Amazon S3', 'amazon-s3-and-cloudfront' ), array( $this, 'attachment_s3_actions_meta_box' ), 'attachment', 'side', 'core' ); + add_meta_box( + 's3-actions', + __( 'Amazon S3', 'amazon-s3-and-cloudfront' ), + array( $this, 'attachment_s3_actions_meta_box' ), + 'attachment', + 'side', + 'core' + ); } /** @@ -4230,15 +4252,22 @@ public function load_media_assets() { wp_enqueue_style( 'as3cf-media-styles', $src, array( 'as3cf-modal' ), $version ); $src = plugins_url( 'assets/js/media' . $suffix . '.js', $this->plugin_file_path ); - wp_enqueue_script( 'as3cf-media-script', $src, array( 'jquery', 'media-views', 'media-grid', 'wp-util' ), $version, true ); + wp_enqueue_script( + 'as3cf-media-script', + $src, + array( 'jquery', 'media-views', 'media-grid', 'wp-util' ), + $version, + true + ); - wp_localize_script( 'as3cf-media-script', + wp_localize_script( + 'as3cf-media-script', 'as3cf_media', array( 'strings' => $this->get_media_action_strings(), 'nonces' => array( 'get_attachment_s3_details' => wp_create_nonce( 'get-attachment-s3-details' ), - ) + ), ) ); } @@ -4347,8 +4376,61 @@ public function remove_size_from_filename( $url, $remove_extension = false ) { $url = preg_replace( '/^(\S+)-[0-9]{1,4}x[0-9]{1,4}(\.[a-zA-Z0-9\.]{2,})?/', '$1$2', $url ); if ( $remove_extension ) { - $parts = pathinfo( $url ); - $url = str_replace( '.' . $parts['extension'], '', $url ); + $ext = pathinfo( $url, PATHINFO_EXTENSION ); + $url = str_replace( ".$ext", '', $url ); + } + + return $url; + } + + /** + * Has the given attachment been uploaded by this instance? + * + * @param int $attachment_id + * + * @return bool + */ + public function attachment_just_uploaded( $attachment_id ) { + if ( is_int( $attachment_id ) && in_array( $attachment_id, $this->uploaded_post_ids ) ) { + return true; + } + + return false; + } + + /** + * Filters the audio shortcode output to remove "&_=NN" params from source.src as it breaks signed URLs. + * + * @param string $html Audio shortcode HTML output. + * @param array $atts Array of audio shortcode attributes. + * @param string $audio Audio file. + * @param int $post_id Post ID. + * @param string $library Media library used for the audio shortcode. + * + * @return string + * + * Note: Depends on 30377.4.diff from https://core.trac.wordpress.org/ticket/30377 + */ + public function wp_audio_shortcode( $html, $atts, $audio, $post_id, $library ) { + $html = preg_replace( '/&_=[0-9]+/', '', $html ); + + return $html; + } + + /** + * Ensure local URL is correct for multisite's non-primary subsites. + * + * @param string $url + * + * @return string + */ + public function maybe_fix_local_subsite_url( $url ) { + $siteurl = trailingslashit( get_option( 'siteurl' ) ); + + if ( is_multisite() && ! $this->is_current_blog( get_current_blog_id() ) && 0 !== strpos( $url, $siteurl ) ) { + // Replace network URL with subsite's URL. + $network_siteurl = trailingslashit( network_site_url() ); + $url = str_replace( $network_siteurl, $siteurl, $url ); } return $url; diff --git a/classes/as3cf-filter.php b/classes/as3cf-filter.php index 097048fe..be4bfc0e 100644 --- a/classes/as3cf-filter.php +++ b/classes/as3cf-filter.php @@ -257,6 +257,8 @@ protected function get_urls_from_content( $content, $cache, &$to_cache ) { $urls = array(); foreach ( $matches as $url ) { + $url = preg_replace( '/[^a-zA-Z0-9]$/', '', $url ); + if ( ! $this->url_needs_replacing( $url ) ) { // URL already correct, skip continue; @@ -269,9 +271,7 @@ protected function get_urls_from_content( $content, $cache, &$to_cache ) { continue; } - $info = pathinfo( $parts['path'] ); - - if ( ! isset( $info['extension'] ) ) { + if ( ! pathinfo( $parts['path'], PATHINFO_EXTENSION ) ) { // URL doesn't have a file extension, continue continue; } diff --git a/classes/as3cf-plugin-compatibility.php b/classes/as3cf-plugin-compatibility.php index 214b393c..796d75de 100644 --- a/classes/as3cf-plugin-compatibility.php +++ b/classes/as3cf-plugin-compatibility.php @@ -93,6 +93,7 @@ function compatibility_init_if_setup() { add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'image_editor_remove_original_image' ), 10, 3 ); add_filter( 'as3cf_get_attached_file', array( $this, 'customizer_crop_download_file' ), 10, 4 ); add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'customizer_crop_remove_original_image' ), 10, 3 ); + add_filter( 'wp_unique_filename', array( $this, 'customizer_crop_unique_filename' ), 10, 3 ); /* * Regenerate Thumbnails @@ -413,7 +414,7 @@ function copy_image_to_server_on_action( $action_key, $ajax, $url, $file, $s3_ob function get_original_image_file( $post_id, $file_path ) { // remove original main image after edit $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true ); - $original_file = trailingslashit( dirname( $file_path ) ) . basename( $meta['file'] ); + $original_file = trailingslashit( dirname( $file_path ) ) . wp_basename( $meta['file'] ); if ( file_exists( $original_file ) ) { return $original_file; } @@ -552,11 +553,15 @@ protected function is_customizer_crop_action() { * * @return string */ - function customizer_crop_download_file( $url, $file, $attachment_id, $s3_object ) { + public function customizer_crop_download_file( $url, $file, $attachment_id, $s3_object ) { if ( false === $this->is_customizer_crop_action() ) { return $url; } + if ( $this->as3cf->attachment_just_uploaded( $attachment_id ) ) { + return $url; + } + if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) { // Return the file if successfully downloaded from S3 return $file; @@ -915,7 +920,7 @@ public function wp_calculate_image_srcset( $sources, $size_array, $image_src, $i } foreach ( $sources as $width => $source ) { - $filename = basename( $source['url'] ); + $filename = wp_basename( $source['url'] ); $size = $this->find_image_size_from_width( $image_meta['sizes'], $width, $filename ); $s3_url = $this->as3cf->get_attachment_s3_url( $attachment_id, $s3object, null, $size, $image_meta ); @@ -948,4 +953,26 @@ protected function find_image_size_from_width( $sizes, $width, $filename ) { return null; } + + /** + * Filters the result when generating a unique file name for a customizer crop. + * + * @param string $filename Unique file name. + * @param string $ext File extension, eg. ".png". + * @param string $dir Directory path. + * + * @return string + */ + public function customizer_crop_unique_filename( $filename, $ext, $dir ) { + if ( false === $this->is_customizer_crop_action() ) { + return $filename; + } + + // Get parent Post ID for cropped image. + $post_id = filter_input( INPUT_POST, 'id', FILTER_VALIDATE_INT ); + + $filename = $this->as3cf->filter_unique_filename( $filename, $post_id ); + + return $filename; + } } diff --git a/classes/as3cf-upgrade-filter-post.php b/classes/as3cf-upgrade-filter-post.php index 0b4fe819..1a8f7903 100644 --- a/classes/as3cf-upgrade-filter-post.php +++ b/classes/as3cf-upgrade-filter-post.php @@ -346,9 +346,9 @@ protected function find_and_replace_attachment_urls( $attachment_id, $where_lowe */ protected function get_find_and_replace_urls( $file_path, $old_url, $new_url, $meta, $backups = '' ) { $url_pairs = array(); - $file_name = basename( $file_path ); - $old_file_name = basename( $old_url ); - $new_file_name = basename( $new_url ); + $file_name = wp_basename( $file_path ); + $old_file_name = wp_basename( $old_url ); + $new_file_name = wp_basename( $new_url ); // Full size image $url_pairs[] = $this->add_url_pair( $file_path, $file_name, $old_url, $old_file_name, $new_url, $new_file_name ); @@ -430,7 +430,7 @@ protected function add_url_pair( $file_path, $file_name, $old_url, $old_file_nam */ protected function maybe_add_encoded_url_pairs( $url_pairs ) { foreach ( $url_pairs as $url_pair ) { - $file_name = basename( $url_pair['old_url'] ); + $file_name = wp_basename( $url_pair['old_url'] ); $encoded_file_name = $this->as3cf->encode_filename_in_path( $file_name ); if ( $file_name !== $encoded_file_name ) { diff --git a/classes/filters/as3cf-local-to-s3.php b/classes/filters/as3cf-local-to-s3.php index da925dc5..f4b29a4e 100644 --- a/classes/filters/as3cf-local-to-s3.php +++ b/classes/filters/as3cf-local-to-s3.php @@ -20,6 +20,7 @@ protected function init() { add_filter( 'the_excerpt', array( $this, 'filter_post' ), 100 ); add_filter( 'content_edit_pre', array( $this, 'filter_post' ) ); add_filter( 'excerpt_edit_pre', array( $this, 'filter_post' ) ); + add_filter( 'as3cf_filter_post_local_to_s3', array( $this, 'filter_post' ) ); // Widgets add_filter( 'widget_text', array( $this, 'filter_widget' ) ); add_filter( 'widget_form_callback', array( $this, 'filter_widget_form' ), 10, 2 ); @@ -140,7 +141,8 @@ public function filter_widget_form( $instance, $class ) { */ protected function url_needs_replacing( $url ) { $uploads = wp_upload_dir(); - $base_url = $this->as3cf->remove_scheme( $uploads['baseurl'] ); + $base_url = $this->as3cf->maybe_fix_local_subsite_url( $uploads['baseurl'] ); + $base_url = $this->as3cf->remove_scheme( $base_url ); if ( false !== strpos( $url, $base_url ) ) { // Local URL, perform replacement diff --git a/classes/filters/as3cf-s3-to-local.php b/classes/filters/as3cf-s3-to-local.php index 8d487d68..c374b223 100644 --- a/classes/filters/as3cf-s3-to-local.php +++ b/classes/filters/as3cf-s3-to-local.php @@ -16,6 +16,7 @@ protected function init() { // Posts add_filter( 'content_save_pre', array( $this, 'filter_post' ) ); add_filter( 'excerpt_save_pre', array( $this, 'filter_post' ) ); + add_filter( 'as3cf_filter_post_s3_to_local', array( $this, 'filter_post' ) ); // Widgets add_filter( 'widget_update_callback', array( $this, 'filter_widget_update' ), 10, 4 ); } @@ -54,6 +55,12 @@ public function filter_widget_update( $instance, $new_instance, $old_instance, $ $to_cache = array(); $instance['text'] = $this->process_content( $instance['text'], $cache, $to_cache ); + // Editing Text Widget in Customizer throws an error if more than one option record is updated. + // Therefore cache updating has to wait until render or edit via Appearance menu. + if ( isset( $_POST['wp_customize'] ) && 'on' === $_POST['wp_customize'] ) { + return $instance; + } + $this->maybe_update_option_cache( $to_cache ); return $instance; diff --git a/classes/upgrades/as3cf-file-sizes.php b/classes/upgrades/as3cf-file-sizes.php index ab44c704..3d2bea93 100644 --- a/classes/upgrades/as3cf-file-sizes.php +++ b/classes/upgrades/as3cf-file-sizes.php @@ -75,11 +75,11 @@ protected function upgrade_item( $attachment ) { $s3client = $this->as3cf->get_s3client( $region, true ); $main_file = $s3object['key']; - $path_parts = pathinfo( $main_file ); + $ext = pathinfo( $main_file, PATHINFO_EXTENSION ); $prefix = trailingslashit( dirname( $s3object['key'] ) ); // Used to search S3 for all files related to an attachment - $search_prefix = $prefix . basename( $main_file, '.' . $path_parts['extension'] ); + $search_prefix = $prefix . wp_basename( $main_file, ".$ext" ); $args = array( 'Bucket' => $s3object['bucket'], diff --git a/languages/amazon-s3-and-cloudfront-en.pot b/languages/amazon-s3-and-cloudfront-en.pot index 14f27846..540f75a6 100644 --- a/languages/amazon-s3-and-cloudfront-en.pot +++ b/languages/amazon-s3-and-cloudfront-en.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: amazon-s3-and-cloudfront\n" "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n" -"POT-Creation-Date: 2017-01-12 08:46-0500\n" +"POT-Creation-Date: 2017-03-13 13:16+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,135 +17,135 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: classes/amazon-s3-and-cloudfront.php:108 +#: classes/amazon-s3-and-cloudfront.php:115 msgid "Offload S3 Lite" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:109 +#: classes/amazon-s3-and-cloudfront.php:116 msgid "S3 and CloudFront" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:215 +#: classes/amazon-s3-and-cloudfront.php:224 #: view/bucket-setting.php:18 msgid "defined in wp-config.php" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:874 +#: classes/amazon-s3-and-cloudfront.php:886 msgid "Upload aborted by filter 'as3cf_pre_upload_attachment'" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:885 +#: classes/amazon-s3-and-cloudfront.php:897 #, php-format msgid "File %s does not exist" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:896 +#: classes/amazon-s3-and-cloudfront.php:908 #, php-format msgid "Mime type %s is not allowed" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:981 +#: classes/amazon-s3-and-cloudfront.php:993 #, php-format msgid "Error uploading %s to S3: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2184 +#: classes/amazon-s3-and-cloudfront.php:2191 msgid "Cheatin’ eh?" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2188 +#: classes/amazon-s3-and-cloudfront.php:2195 msgid "You do not have sufficient permissions to access this page." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2194 +#: classes/amazon-s3-and-cloudfront.php:2201 msgid "No bucket name provided." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2453 +#: classes/amazon-s3-and-cloudfront.php:2465 msgid "Error Getting Bucket Region" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2454 +#: classes/amazon-s3-and-cloudfront.php:2466 #, php-format msgid "There was an error attempting to get the region of the bucket %s: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2574 +#: classes/amazon-s3-and-cloudfront.php:2585 msgid "" "This is a test file to check if the user has write permission to S3. Delete " "me if found." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2606 +#: classes/amazon-s3-and-cloudfront.php:2617 #, php-format msgid "" "There was an error attempting to check the permissions of the bucket %s: %s" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2663 +#: classes/amazon-s3-and-cloudfront.php:2674 msgid "Error creating bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2664 +#: classes/amazon-s3-and-cloudfront.php:2675 msgid "Bucket name too short." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2665 +#: classes/amazon-s3-and-cloudfront.php:2676 msgid "Bucket name too long." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2666 +#: classes/amazon-s3-and-cloudfront.php:2677 msgid "" "Invalid character. Bucket names can contain lowercase letters, numbers, " "periods and hyphens." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2667 +#: classes/amazon-s3-and-cloudfront.php:2678 msgid "Error saving bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2668 +#: classes/amazon-s3-and-cloudfront.php:2679 msgid "Error fetching buckets" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2669 +#: classes/amazon-s3-and-cloudfront.php:2680 msgid "Error getting URL preview: " msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2670 +#: classes/amazon-s3-and-cloudfront.php:2681 msgid "The changes you made will be lost if you navigate away from this page" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2671 +#: classes/amazon-s3-and-cloudfront.php:2682 msgid "Getting diagnostic info..." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2672 +#: classes/amazon-s3-and-cloudfront.php:2683 msgid "Error getting diagnostic info: " msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2740 +#: classes/amazon-s3-and-cloudfront.php:2751 msgid "Cheatin' eh?" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2848 +#: classes/amazon-s3-and-cloudfront.php:2858 msgctxt "Show the media library tab" msgid "Media Library" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:2849 +#: classes/amazon-s3-and-cloudfront.php:2859 msgctxt "Show the support tab" msgid "Support" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3066 +#: classes/amazon-s3-and-cloudfront.php:3076 #, php-format msgid "" "WP Offload S3 — The file %s has been given %s " "permissions on Amazon S3." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3085 +#: classes/amazon-s3-and-cloudfront.php:3095 msgid "" "WP Offload S3 Requirement Missing — Looks like you " "don't have an image manipulation library installed on this server and " @@ -153,11 +153,11 @@ msgid "" "Please setup GD or ImageMagick." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3759 +#: classes/amazon-s3-and-cloudfront.php:3774 msgid "Quick Start Guide" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3761 +#: classes/amazon-s3-and-cloudfront.php:3776 #, php-format msgid "" "Looks like we don't have write access to this bucket. It's likely that the " @@ -166,7 +166,7 @@ msgid "" "correctly." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3763 +#: classes/amazon-s3-and-cloudfront.php:3778 #, php-format msgid "" "Looks like we don't have access to the buckets. It's likely that the user " @@ -174,39 +174,39 @@ msgid "" "Please see our %s for instructions on setting up permissions correctly." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3914 +#: classes/amazon-s3-and-cloudfront.php:3929 msgid "WP Offload S3 Activation" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3915 +#: classes/amazon-s3-and-cloudfront.php:3930 msgid "" "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've " "automatically deactivated WP Offload S3 Lite." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3917 +#: classes/amazon-s3-and-cloudfront.php:3932 msgid "WP Offload S3 Lite Activation" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3918 +#: classes/amazon-s3-and-cloudfront.php:3933 msgid "" "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've " "automatically deactivated WP Offload S3." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:3992 +#: classes/amazon-s3-and-cloudfront.php:4007 msgid "More info" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4083 +#: classes/amazon-s3-and-cloudfront.php:4098 msgid "this doc" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4085 +#: classes/amazon-s3-and-cloudfront.php:4100 msgid "WP Offload S3 Feature Removed" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4086 +#: classes/amazon-s3-and-cloudfront.php:4101 #, php-format msgid "" "You had the \"Always non-SSL\" option selected in your settings, but we've " @@ -217,32 +217,32 @@ msgid "" "to the old behavior." msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4114 -#: classes/amazon-s3-and-cloudfront.php:4207 +#: classes/amazon-s3-and-cloudfront.php:4131 +#: classes/amazon-s3-and-cloudfront.php:4229 msgid "Amazon S3" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4208 +#: classes/amazon-s3-and-cloudfront.php:4230 msgctxt "Amazon S3 bucket" msgid "Bucket" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4209 +#: classes/amazon-s3-and-cloudfront.php:4231 msgctxt "Path to file on Amazon S3" msgid "Path" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4210 +#: classes/amazon-s3-and-cloudfront.php:4232 msgctxt "Location of Amazon S3 bucket" msgid "Region" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4211 +#: classes/amazon-s3-and-cloudfront.php:4233 msgctxt "Access control list of the file on Amazon S3" msgid "Access" msgstr "" -#: classes/amazon-s3-and-cloudfront.php:4212 +#: classes/amazon-s3-and-cloudfront.php:4234 msgid "URL" msgstr "" @@ -254,15 +254,15 @@ msgstr "" msgid "Invalid notice ID." msgstr "" -#: classes/as3cf-plugin-compatibility.php:192 +#: classes/as3cf-plugin-compatibility.php:193 msgid "WP Offload S3 Compatibility Addons" msgstr "" -#: classes/as3cf-plugin-compatibility.php:194 +#: classes/as3cf-plugin-compatibility.php:195 msgid "compatibility addons" msgstr "" -#: classes/as3cf-plugin-compatibility.php:195 +#: classes/as3cf-plugin-compatibility.php:196 #, php-format msgid "" "To get WP Offload S3 to work with certain 3rd party plugins, you might need " @@ -271,7 +271,7 @@ msgid "" "about each addon to determine if you need it or not." msgstr "" -#: classes/as3cf-plugin-compatibility.php:201 +#: classes/as3cf-plugin-compatibility.php:202 #, php-format msgid "" "You will need to purchase a license to get access to these addons. If you're " @@ -279,17 +279,17 @@ msgid "" "to %s." msgstr "" -#: classes/as3cf-plugin-compatibility.php:202 +#: classes/as3cf-plugin-compatibility.php:203 msgid "View Licenses" msgstr "" -#: classes/as3cf-plugin-compatibility.php:664 +#: classes/as3cf-plugin-compatibility.php:669 #, php-format msgid "The local directory %s does not exist and could not be created." msgstr "" -#: classes/as3cf-plugin-compatibility.php:665 -#: classes/as3cf-plugin-compatibility.php:677 +#: classes/as3cf-plugin-compatibility.php:670 +#: classes/as3cf-plugin-compatibility.php:682 #: classes/upgrades/as3cf-meta-wp-error.php:81 #, php-format msgid "There was an error attempting to download the file %s from S3: %s" diff --git a/readme.txt b/readme.txt index 228af587..7c482d81 100644 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: bradt, deliciousbrains Tags: uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront Requires at least: 4.4 -Tested up to: 4.7.1 -Stable tag: 1.1.5 +Tested up to: 4.7.3 +Stable tag: 1.1.6 License: GPLv3 Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery. @@ -65,6 +65,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin == Changelog == += WP Offload S3 Lite 1.1.6 - 2017-03-13 = +* New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/) +* New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content +* Improvement: Ensure files uploaded using `media_handle_sideload` have unique filename on S3 when 'Remove Files From Server' enabled +* Bug fix: Files uploaded to S3 with empty filenames when the filename started with non-latin characters +* Bug fix: Audio files with private ACL not working with WordPress's default media player +* Bug fix: S3 API version not passed to S3 client +* Bug fix: Content added to text widgets via the Customizer not saved +* Bug fix: Original file not removed locally when cropped via the Customizer and 'Remove Files From Server' enabled +* Bug fix: Incorrect Media Library URLs saved to the database when WordPress installed in a subdirectory + = WP Offload S3 Lite 1.1.5 - 2017-01-12 = * Improvement: Filter custom CSS - S3 URLs will no longer be saved to the database * Bug fix: PDF previews have incorrect MIME type diff --git a/view/attachment-metabox.php b/view/attachment-metabox.php index 4d5938de..3f11f49e 100644 --- a/view/attachment-metabox.php +++ b/view/attachment-metabox.php @@ -6,28 +6,28 @@
get_media_action_strings( 'bucket' ); ?>:
- +
get_media_action_strings( 'key' ); ?>:
- +
get_media_action_strings( 'region' ); ?>:
-
+
get_media_action_strings( 'acl' ); ?>:
-
+
get_acl_value_string( $s3object['acl'] ); ?>
@@ -39,14 +39,14 @@
diff --git a/view/wordpress-org-support.php b/view/wordpress-org-support.php index 20114c1d..0ca032ee 100644 --- a/view/wordpress-org-support.php +++ b/view/wordpress-org-support.php @@ -3,7 +3,7 @@

WordPress.org support forum. Response time can range from a few days to a few weeks and will likely be from a non-developer.', 'amazon-s3-and-cloudfront'), 'https://wordpress.org/plugins/amazon-s3-and-cloudfront/' ); ?>

-

timely response via email from a developer who works on this plugin, upgrade and send us an email.', 'amazon-s3-and-cloudfront' ), 'https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&utm_medium=web&utm_content=support-tab&utm_campaign=freeplugin-as3cf' ); ?>

+

timely response via email from a developer who works on this plugin, upgrade and send us an email.', 'amazon-s3-and-cloudfront' ), 'https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&utm_medium=web&utm_content=support-tab&utm_campaign=os3-free-plugin' ); ?>

submit an issue on GitHub.', 'amazon-s3-and-cloudfront' ), 'https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/issues' ); ?>

diff --git a/wordpress-s3.php b/wordpress-s3.php index 1de9631c..b1044e5e 100644 --- a/wordpress-s3.php +++ b/wordpress-s3.php @@ -4,7 +4,7 @@ Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/ Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery. Author: Delicious Brains -Version: 1.1.5 +Version: 1.1.6 Author URI: http://deliciousbrains.com/ Network: True Text Domain: amazon-s3-and-cloudfront @@ -26,7 +26,7 @@ // Then completely rewritten. */ -$GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '1.1.5'; +$GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '1.1.6'; $aws_plugin_version_required = '1.0.1'; @@ -76,6 +76,11 @@ function as3cf_init( $aws ) { require_once $abspath . '/classes/as3cf-stream-wrapper.php'; require_once $abspath . '/classes/as3cf-plugin-compatibility.php'; require_once $abspath . '/classes/amazon-s3-and-cloudfront.php'; + + // Autoloader + require_once $abspath . '/wp-offload-s3-autoloader.php'; + new WP_Offload_S3_Autoloader( 'WP_Offload_S3', $abspath ); + $as3cf = new Amazon_S3_And_CloudFront( __FILE__, $aws ); } diff --git a/wp-offload-s3-autoloader.php b/wp-offload-s3-autoloader.php new file mode 100644 index 00000000..7a280c5a --- /dev/null +++ b/wp-offload-s3-autoloader.php @@ -0,0 +1,94 @@ +prefix = $prefix; + $this->abspath = $abspath; + + spl_autoload_register( array( $this, 'autoloader' ) ); + } + + /** + * Autoloader. + * + * @param string $class_name + */ + public function autoloader( $class_name ) { + if ( ! $this->class_belongs_to_plugin( $class_name ) ) { + return; + } + + $path = $this->get_classes_directory() . $this->get_class_path( $class_name ); + + if ( file_exists( $path ) ) { + require_once $path; + } + } + + /** + * Class belong to plugin. + * + * @param string $class_name + * + * @return bool + */ + protected function class_belongs_to_plugin( $class_name ) { + if ( 0 !== strpos( $class_name, $this->vendor . '\\' . $this->prefix . '\\' ) ) { + return false; + } + + return true; + } + + /** + * Get class path. + * + * @param string $class_name + * + * @return string + */ + protected function get_class_path( $class_name ) { + $parts = explode( '\\', strtolower( $class_name ) ); + $parts = array_slice( $parts, 2 ); + + $filename = implode( DIRECTORY_SEPARATOR, $parts ) . '.php'; + + return str_replace( '_', '-', strtolower( $filename ) ); + } + + /** + * Get classes directory. + * + * @return string + */ + protected function get_classes_directory() { + return $this->abspath . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR; + } + + } +}