WP Offload S3 Lite - Version 1.1.1

Version Description

= 1.1 = This is a major change, which ensures S3 URLs are no longer saved in post content. Instead, local URLs are filtered on page generation and replaced with the S3 version. If you depend on the S3 URLs being stored in post content you will need to make modifications to support this version.

= 0.6 = This version requires PHP 5.3.3+ and the Amazon Web Services plugin

Download this release

Release Info

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

Code changes from version 1.0.5 to 1.1.1

README.md CHANGED
@@ -2,8 +2,8 @@
2
  **Contributors:** bradt, deliciousbrains
3
  **Tags:** uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
  **Requires at least:** 4.4
5
- **Tested up to:** 4.6
6
- **Stable tag:** 1.0.5
7
  **License:** GPLv3
8
 
9
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
@@ -21,7 +21,6 @@ If you're adding this plugin to a site that's been around for a while, your exis
21
  **PRO Upgrade with Email Support and More Features**
22
 
23
  * Upload existing Media Library to Amazon S3
24
- * Find & replace file URLs in content
25
  * Control Amazon S3 files from the Media Library
26
  * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#assets-addon) - Serve your CSS & JS from Amazon S3/CloudFront
27
  * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#woocommerce-addon)
@@ -62,25 +61,45 @@ You can see the minimum requirements [here](https://deliciousbrains.com/wp-offlo
62
 
63
  ## Upgrade Notice ##
64
 
 
 
 
65
  ### 0.6 ###
66
  This version requires PHP 5.3.3+ and the Amazon Web Services plugin
67
 
68
  ## Changelog ##
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  ### WP Offload S3 Lite 1.0.5 - 2016-09-01 ###
71
  * New: Compatibility with WordPress 4.6
72
  * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/)
73
 
74
  ### WP Offload S3 Lite 1.0.4 - 2016-05-30 ###
75
- * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting.
76
- * New: `as3cf_cloudfront_path_parts` filter allows changing served CloudFront path (useful when distribution pulls subdirectory).
77
- * Improvement: Better compatibility with non-standard notices from other plugins and themes.
78
- * Improvement: Added basic auth and proxy info to diagnostic info.
79
- * Improvement: Added `allow_url_fopen` status to diagnostic info.
80
- * Improvement: Added memory usage to diagnostic info.
81
- * Improvement: Ensure notice text is 800px or less in width.
82
- * Improvement: Reduced database queries on settings screen.
83
- * Bug fix: Properly handle _wp_attachment_data metadata when it is a serialized WP_Error.
84
 
85
  ### WP Offload S3 Lite 1.0.3 - 2016-03-23 ###
86
  * Bug fix: Don't replace srcset URLs when Rewrite File URLs option disabled
@@ -101,7 +120,7 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
101
  * Improvement: Far future expiration header set by default
102
  * Improvement: Newly created bucket now immediately appears in the bucket list
103
  * Improvement: Cleanup user meta on uninstall
104
- * Improvement: WP Retina 2x integration [removed](https://deliciousbrains.com/wp-offload-s3/doc/copy-hidpi-2x-images-support/)
105
  * Bug fix: Year/Month folder structure on S3 not created if the 'Organise my uploads into month and year-based folders' WordPress setting is disabled
106
  * Bug fix: Responsive srcset PHP notices
107
  * Bug fix: Compatibility addon notices displayed to non-admin users
2
  **Contributors:** bradt, deliciousbrains
3
  **Tags:** uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
  **Requires at least:** 4.4
5
+ **Tested up to:** 4.6.1
6
+ **Stable tag:** 1.1.1
7
  **License:** GPLv3
8
 
9
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
21
  **PRO Upgrade with Email Support and More Features**
22
 
23
  * Upload existing Media Library to Amazon S3
 
24
  * Control Amazon S3 files from the Media Library
25
  * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#assets-addon) - Serve your CSS & JS from Amazon S3/CloudFront
26
  * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#woocommerce-addon)
61
 
62
  ## Upgrade Notice ##
63
 
64
+ ### 1.1 ###
65
+ This is a major change, which ensures S3 URLs are no longer saved in post content. Instead, local URLs are filtered on page generation and replaced with the S3 version. If you depend on the S3 URLs being stored in post content you will need to make modifications to support this version.
66
+
67
  ### 0.6 ###
68
  This version requires PHP 5.3.3+ and the Amazon Web Services plugin
69
 
70
  ## Changelog ##
71
 
72
+ ### WP Offload S3 Lite 1.1.1 - 2016-10-17 ###
73
+ * New: Filter post excerpts - S3 URLs will no longer be saved to the database
74
+ * Bug fix: PHP 5.3 Fatal error: Using $this when not in object context
75
+ * Bug fix: Query string parameters incorrectly encoded for Media Library items
76
+
77
+ ### WP Offload S3 Lite 1.1 - 2016-09-29 ###
78
+ * New: Filter post content. S3 URLs will no longer be saved to the database
79
+ * New: Upgrade routine to replace all S3 URLs in content with local URLs
80
+ * New: Support for theme custom logos
81
+ * New: Control the ACL for intermediate image sizes using the `as3cf_upload_acl_sizes` filter
82
+ * Bug fix: File names containing special characters double encoded
83
+ * Bug fix: `srcset` not working for file names containing special characters
84
+ * Bug fix: Incorrect placeholder text for 'Path' option
85
+ * Bug fix: Objects in root of bucket not deleted when removed from the Media Library
86
+ * Bug fix: No longer use deprecated functions in WordPress 4.6
87
+ * Bug fix: Don't delete local file when 'Remove Files From Server' enabled and upload to S3 fails
88
+
89
  ### WP Offload S3 Lite 1.0.5 - 2016-09-01 ###
90
  * New: Compatibility with WordPress 4.6
91
  * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/)
92
 
93
  ### WP Offload S3 Lite 1.0.4 - 2016-05-30 ###
94
+ * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
95
+ * New: `as3cf_cloudfront_path_parts` filter allows changing served CloudFront path (useful when distribution pulls subdirectory)
96
+ * Improvement: Better compatibility with non-standard notices from other plugins and themes
97
+ * Improvement: Added basic auth and proxy info to diagnostic info
98
+ * Improvement: Added `allow_url_fopen` status to diagnostic info
99
+ * Improvement: Added memory usage to diagnostic info
100
+ * Improvement: Ensure notice text is 800px or less in width
101
+ * Improvement: Reduced database queries on settings screen
102
+ * Bug fix: Properly handle _wp_attachment_data metadata when it is a serialized WP_Error
103
 
104
  ### WP Offload S3 Lite 1.0.3 - 2016-03-23 ###
105
  * Bug fix: Don't replace srcset URLs when Rewrite File URLs option disabled
120
  * Improvement: Far future expiration header set by default
121
  * Improvement: Newly created bucket now immediately appears in the bucket list
122
  * Improvement: Cleanup user meta on uninstall
123
+ * Improvement: WP Retina 2x integration removed
124
  * Bug fix: Year/Month folder structure on S3 not created if the 'Organise my uploads into month and year-based folders' WordPress setting is disabled
125
  * Bug fix: Responsive srcset PHP notices
126
  * Bug fix: Compatibility addon notices displayed to non-admin users
assets/css/attachment.css ADDED
@@ -0,0 +1 @@
 
1
+ #s3-actions.postbox .inside{margin:0;padding:0}#s3-actions.postbox a,#s3-actions.postbox a:hover{text-decoration:none}#s3-actions.postbox .s3-details{padding:6px 0}#s3-actions.postbox .s3-details .misc-pub-section{clear:both;float:left;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}#s3-actions.postbox .s3-details .misc-pub-section .s3-key{float:left;width:20%}#s3-actions.postbox .s3-details .misc-pub-section .s3-value{font-weight:bold;float:left;width:80%}#s3-actions.postbox .s3-details .not-copied{color:#666666}#s3-actions.postbox .s3-actions{padding:10px;clear:both;border-top:1px solid #ddd;border-bottom:1px solid #ddd;background:#f5f5f5}#s3-actions.postbox .s3-actions .copy-action{text-align:right;float:right;line-height:23px}#s3-actions.postbox .s3-actions .remove-action{line-height:28px;vertical-align:middle;text-align:left;float:left}#s3-actions.postbox .s3-actions .remove-action a.local-warning{color:#a00}#s3-actions.postbox .s3-actions .remove-action a.local-warning:hover{color:#f00}
assets/css/media.css ADDED
@@ -0,0 +1 @@
 
1
+ body.as3cf-pro .attachments-browser .media-toolbar-secondary{max-width:100%}.as3cfpro_remove a.local-warning{color:#a00}.as3cfpro_remove a.local-warning:hover{color:#f00;text-decoration:none;border:none}.media-modal a.local-warning{color:#bc0b0b}.media-modal a.local-warning:hover{color:red}.attachment-info .attachment-s3-details{font-weight:bold;margin-bottom:5px}
assets/js/media.js ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var test = {};
2
+
3
+ (function( $, _ ) {
4
+
5
+ // Local reference to the WordPress media namespace.
6
+ var media = wp.media;
7
+
8
+ // Local instance of the Attachment Details TwoColumn used in the edit attachment modal view
9
+ var wpAttachmentDetailsTwoColumn = media.view.Attachment.Details.TwoColumn;
10
+
11
+ /**
12
+ * Add S3 details to attachment.
13
+ */
14
+ media.view.Attachment.Details.TwoColumn = wpAttachmentDetailsTwoColumn.extend( {
15
+ events: function() {
16
+ return _.extend( {}, wpAttachmentDetailsTwoColumn.prototype.events, {
17
+ 'click .local-warning': 'confirmS3Removal',
18
+ 'click #as3cfpro-toggle-acl': 'toggleACL'
19
+ } );
20
+ },
21
+
22
+ render: function() {
23
+ // Retrieve the S3 details for the attachment
24
+ // before we render the view
25
+ this.fetchS3Details( this.model.get( 'id' ) );
26
+ },
27
+
28
+ fetchS3Details: function( id ) {
29
+ wp.ajax.send( 'as3cf_get_attachment_s3_details', {
30
+ data: {
31
+ _nonce: as3cf_media.nonces.get_attachment_s3_details,
32
+ id: id
33
+ }
34
+ } ).done( _.bind( this.renderView, this ) );
35
+ },
36
+
37
+ renderView: function( response ) {
38
+ // Render parent media.view.Attachment.Details
39
+ wpAttachmentDetailsTwoColumn.prototype.render.apply( this );
40
+
41
+ this.renderActionLinks( response );
42
+ this.renderS3Details( response );
43
+ },
44
+
45
+ renderActionLinks: function( response ) {
46
+ var links = ( response && response.links ) || [];
47
+ var $actionsHtml = this.$el.find( '.actions' );
48
+ var $s3Actions = $( '<div />', {
49
+ 'class': 's3-actions'
50
+ } );
51
+
52
+ var s3Links = [];
53
+ _( links ).each( function( link ) {
54
+ s3Links.push( link );
55
+ } );
56
+
57
+ $s3Actions.append( s3Links.join( ' | ' ) );
58
+ $actionsHtml.append( $s3Actions );
59
+ },
60
+
61
+ renderS3Details: function( response ) {
62
+ if ( ! response || ! response.s3object ) {
63
+ return;
64
+ }
65
+ var $detailsHtml = this.$el.find( '.attachment-info .details' );
66
+ var html = this.generateDetails( response, [ 'bucket', 'key', 'region', 'acl' ] );
67
+ $detailsHtml.append( html );
68
+ },
69
+
70
+ generateDetails: function( response, keys ) {
71
+ var html = '';
72
+ var template = _.template( '<div class="<%= key %>"><strong><%= label %>:</strong> <%= value %></div>' );
73
+
74
+ _( keys ).each( function( key ) {
75
+ if ( response.s3object[ key ] ) {
76
+ var value = response.s3object[ key ];
77
+
78
+ if ( 'acl' === key ) {
79
+ value = response.s3object[ key ]['name'];
80
+
81
+ if ( response.acl_toggle ) {
82
+ var acl_template = _.template( '<a href="#" id="as3cfpro-toggle-acl" title="<%= title %>" data-currentACL="<%= acl %>"><%= value %></a>' );
83
+
84
+ value = acl_template( {
85
+ title: response.s3object[ key ][ 'title' ],
86
+ acl: response.s3object[ key ][ 'acl' ],
87
+ value: value
88
+ } );
89
+ }
90
+ }
91
+
92
+ html += template( {
93
+ key: key,
94
+ label: as3cf_media.strings[ key ],
95
+ value: value
96
+ } );
97
+ }
98
+ } );
99
+
100
+ return html;
101
+ },
102
+
103
+ confirmS3Removal: function( event ) {
104
+ if ( ! confirm( as3cfpro_media.strings.local_warning ) ) {
105
+ event.preventDefault();
106
+ event.stopImmediatePropagation();
107
+ return false;
108
+ }
109
+ },
110
+
111
+ toggleACL: function( event ) {
112
+ event.preventDefault();
113
+
114
+ var toggle = $( '#as3cfpro-toggle-acl' );
115
+ var currentACL = toggle.attr( 'data-currentACL' );
116
+ var newACL = as3cfpro_media.settings.private_acl;
117
+
118
+ toggle.hide();
119
+ toggle.after( '<span id="as3cfpro-updating">' + as3cfpro_media.strings.updating_acl + '</span>' );
120
+
121
+ if ( currentACL === as3cfpro_media.settings.private_acl ) {
122
+ newACL = as3cfpro_media.settings.default_acl;
123
+ }
124
+
125
+ wp.ajax.send( 'as3cfpro_update_acl', {
126
+ data: {
127
+ _nonce: as3cfpro_media.nonces.update_acl,
128
+ id: this.model.get( 'id' ),
129
+ acl: newACL
130
+ }
131
+ } )
132
+ .done( _.bind( this.updateACL, this ) )
133
+ .fail( _.bind( this.renderACLError, this ) );
134
+ },
135
+
136
+ renderACLError: function() {
137
+ $( '#as3cfpro-updating' ).remove();
138
+ $( '#as3cfpro-toggle-acl' ).show();
139
+ alert( as3cfpro_media.strings.change_acl_error );
140
+ },
141
+
142
+ updateACL: function( response ) {
143
+ if ( 'undefined' === typeof response.acl_display || 'undefined' === typeof response.title || 'undefined' === typeof response.acl ) {
144
+ this.renderACLError();
145
+
146
+ return;
147
+ }
148
+
149
+ var toggle = $( '#as3cfpro-toggle-acl' );
150
+
151
+ $( '#as3cfpro-updating' ).remove();
152
+
153
+ toggle.text( response.acl_display );
154
+ toggle.attr( 'title', response.title );
155
+ toggle.attr( 'data-currentACL', response.acl );
156
+ toggle.show();
157
+ }
158
+ } );
159
+
160
+ })( jQuery, _ );
assets/js/media.min.js ADDED
@@ -0,0 +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("<div />",{"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('<div class="<%= key %>"><strong><%= label %>:</strong> <%= value %></div>');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('<a href="#" id="as3cfpro-toggle-acl" title="<%= title %>" data-currentACL="<%= acl %>"><%= value %></a>');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){return confirm(as3cfpro_media.strings.local_warning)?void 0:(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('<span id="as3cfpro-updating">'+as3cfpro_media.strings.updating_acl+"</span>"),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,_);
assets/js/modal.min.js CHANGED
@@ -1 +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('<div id="as3cf-overlay"></div>');var h=a("#as3cf-overlay");h.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>');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);
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]?!0:!!a(c).length},c.open=function(c,e,f){var g=b(c);a("body").append('<div id="as3cf-overlay"></div>');var h=a("#as3cf-overlay");h.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>');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?!1:void c.close()})}),c}(jQuery);
assets/js/script.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b){function c(b){return a("#"+b+" .as3cf-main-settings form").find("input:not(.no-compare)").serialize()}function d(a){var b=k.find("#"+a),c=b.find("input[type=checkbox]");b.toggleClass("on").find("span").toggleClass("checked");var d=b.find("span.on").hasClass("checked");c.attr("checked",d).trigger("change")}function e(b){var c=b.next(".as3cf-validation-error"),d=a("#"+k.attr("id")+' form button[type="submit"]'),e=/[^a-zA-Z0-9\.\-]/;e.test(b.val())?(c.show(),d.attr("disabled",!0)):(c.hide(),d.attr("disabled",!1))}function f(){var c=a("#"+b.prefix+"-bucket").val(),d=k.find('input[name="object-prefix"]'),e=d.val();""!==e&&(e="&prefix="+encodeURIComponent(e));var f=as3cf.aws_bucket_link+c+e;a("#"+b.prefix+"-view-bucket").attr("href",f)}function g(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf.nonces.get_url_preview};a.each(a("#tab-"+as3cf.tabs.defaultTab+" .as3cf-main-settings form").serializeArray(),function(c,d){var e=d.name,f=d.value;e=e.replace("[]",""),b[e]=void 0===b[e]?f:a.isArray(b[e])?b[e].concat(f):[b[e],f]}),b.action="as3cf-get-url-preview",a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){alert(as3cf.strings.get_url_preview_error+c)},success:function(b,c,d){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function h(a){as3cf.buckets.bucketSelectLock=!1}function i(){a("#remove-local-file").is(":checked")&&a("#serve-from-s3").is(":not(:checked)")?a("#as3cf-lost-files-notice").show():a("#as3cf-lost-files-notice").hide()}function j(){a("#remove-local-file").is(":checked")?a("#as3cf-remove-local-notice").show():a("#as3cf-remove-local-notice").hide()}var k,l={},m=/[^a-z0-9.-]/,n=!1,o=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){c=as3cf.tabs.sanitizeHash(c),o.hide(),k=a("#tab-"+c),k.show(),a(".nav-tab").removeClass("nav-tab-active"),a('a.nav-tab[data-tab="'+c+'"]').addClass("nav-tab-active"),a(".aws-main").attr("data-tab",c),k.attr("data-prefix")&&(b.prefix=k.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show"),"support"===c&&as3cf.tabs.getDiagnosticInfo()},getDiagnosticInfo:function(){var b=a(".debug-log-textarea");b.html(as3cf.strings.get_diagnostic_info);var c={action:"as3cf-get-diagnostic-info",_nonce:as3cf.nonces.get_diagnostic_info};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:c,error:function(a,c,d){b.html(d)},success:function(a,c,d){"undefined"!=typeof a.success?b.html(a.diagnostic_info):(b.html(as3cf.strings.get_diagnostic_info_error),b.append(a.error))}})},sanitizeHash:function(b){var c=a("#tab-"+b);return 0===c.length&&(b=as3cf.tabs.defaultTab),b}},as3cf.buckets={validLength:3,bucketSelectLock:!1,loadList:function(c){"undefined"==typeof c&&(c=!1);var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-list"),e=a("#"+b.prefix+"-bucket").val();if(!1===c&&d.find("li").length>1)return a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"),void this.scrollToSelected();d.html('<li class="loading">'+d.attr("data-working")+"</li>");var f={action:b.prefix+"-get-buckets",_nonce:window[b.prefix.replace(/-/g,"_")].nonces.get_buckets},g=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:f,error:function(a,b,c){d.html(""),g.showError(as3cf.strings.get_buckets_error,c,"as3cf-bucket-select")},success:function(b,c,f){d.html(""),"undefined"!=typeof b.success?(a(".as3cf-bucket-error").hide(),a(b.buckets).each(function(a,b){var c=b.Name===e?"selected":"";d.append('<li><a class="'+c+'" href="#" data-bucket="'+b.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+b.Name+'</span><span class="spinner"></span></span></a></li>')}),g.scrollToSelected()):g.showError(as3cf.strings.get_buckets_error,b.error,"as3cf-bucket-select")}})},scrollToSelected:function(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a(".as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}},resetModal:function(){var c=a(".as3cf-bucket-container."+b.prefix);!1===k.hasClass("as3cf-has-bucket")||"manual"===a("#"+b.prefix+"-bucket-select").val()?(c.find(".as3cf-bucket-manual").show().siblings().hide(),c.find(".bucket-actions.manual").show().siblings(".bucket-actions").hide()):(c.find(".as3cf-bucket-select").show().siblings().hide(),c.find(".bucket-actions.select").show().siblings(".bucket-actions").hide(),this.loadList(n),n=!1),c.find(".as3cf-bucket-error").hide();var d=a("#"+b.prefix+"-bucket").val();c.find(".as3cf-bucket-manual .as3cf-bucket-name").val(d),this.bucketSelectLock=!1},saveManual:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find("button[type=submit]"),f=d.val(),g=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),k.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var h={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(a,b,c){e.text(g),i.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c,d,h){e.text(g),e.prop("disabled",!1),"undefined"!=typeof c.success?(i.set(f,c.region,c.can_write),a("#"+b.prefix+"-bucket-select").val("manual"),a(".as3cf-bucket-list a").removeClass("selected").filter('[data-bucket="'+f+'"]').addClass("selected"),n=!0):i.showError(as3cf.strings.save_bucket_error,c.error,"as3cf-bucket-manual")}})},saveSelected:function(c){var d=a(".as3cf-bucket-list");if(!this.bucketSelectLock){if(this.bucketSelectLock=!0,c.hasClass("selected"))return k.addClass("as3cf-has-bucket"),void b.close();var e=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),c.addClass("selected"),d.addClass("saving"),c.find(".spinner").show().css("visibility","visible");var f=c.attr("data-bucket"),g={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},h=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(b,c,f){d.removeClass("saving"),h.showError(as3cf.strings.save_bucket_error,f,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected")},success:function(g,i,j){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(h.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(h.showError(as3cf.strings.save_bucket_error,g.error,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"))}})}},disabledButtons:function(){if(0!==a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form").length){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").attr("disabled",!1),d.find(".as3cf-bucket-name").val().length<3?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").attr("disabled",!1)}},showError:function(b,c,d){var e=a(".as3cf-bucket-container").children(":visible"),f=e.find(".as3cf-bucket-error");d="undefined"==typeof d?null:d,d&&!e.hasClass(d)||(f.find("span.title").html(b+" &mdash;"),f.find("span.message").html(c),f.show(),this.bucketSelectLock=!1)},set:function(e,i,j){var m=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),n=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&0===n.text().trim().length){d("copy-to-s3-wrap"),d("serve-from-s3-wrap");var o=k.attr("id");l[o]=c(o)}a(".as3cf-error.fatal").hide(),n.text(e),m.find(".as3cf-bucket-name").val(e),a("#"+b.prefix+"-bucket").val(e),a("#"+b.prefix+"-region").val(i),a(".updated").not(".as3cf-notice").show(),k.addClass("as3cf-has-bucket"),k.find(".as3cf-can-write-error").toggle(!j),k.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&g(),f(),b.close(h)},create:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find(".bucket-create-region"),f=c.find("button[type=submit]"),g=d.val(),h=f.text();a(".as3cf-bucket-error").hide(),f.text(f.attr("data-working")),f.prop("disabled",!0);var i={action:b.prefix+"-create-bucket",bucket_name:g,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.create_bucket};e.val()&&(i.region=e.val());var j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){f.text(h),j.showError(as3cf.strings.create_bucket_error,c,"as3cf-bucket-create")},success:function(b,c,e){f.text(h),f.prop("disabled",!1),"undefined"!=typeof b.success?(j.set(g,b.region,b.can_write),a(".as3cf-bucket-select-region").hide(),a(".as3cf-bucket-select-region").removeAttr("selected"),d.val(""),f.attr("disabled",!0),n=!0):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return!(a.length<3||a.length>63)&&!0!==m.test(a)},updateNameNotice:function(b){var c=null;!0===m.test(b)?c=as3cf.strings.create_bucket_invalid_chars:b.length<3?c=as3cf.strings.create_bucket_name_short:b.length>63&&(c=as3cf.strings.create_bucket_name_long),c&&b.length>0?a(".as3cf-invalid-bucket-name").html(c):a(".as3cf-invalid-bucket-name").html("")}},a(document).ready(function(){var h=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(h),window.location.hash){var m=window.location.hash.substring(1);as3cf.tabs.toggle(m,!0)}else k=a("#tab-"+as3cf.tabs.defaultTab),a(".aws-main").attr("data-tab",as3cf.tabs.defaultTab);a(".aws-main").on("click",".nav-tab",function(b){if(b.preventDefault(),!a(this).hasClass("nav-tab-active")){var c=a(this).attr("data-tab");as3cf.tabs.toggle(c),"media"===c?(window.location.hash="","function"==typeof window.history.replaceState&&"#"===window.location.href.slice(-1)&&history.replaceState({},"",window.location.href.slice(0,-1))):window.location.hash=c}}),o.length&&o.each(function(a,b){l[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(l)){var b=k.attr("id");return c(b)!==l[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(b){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(b){a(this).hasClass("disabled")||d(a(this).attr("id"))}),o.on("change",".sub-toggle",function(b){var c=a(this).attr("id");a(".as3cf-setting."+c).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(b){var c=a(this).closest('input:radio[name="domain"]:checked'),d=c.val(),e=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),f="cloudfront"===d;e.toggleClass("hide",!f)}),a(".url-preview").on("change","input",function(a){g()}),i(),a("#serve-from-s3,#remove-local-file").on("change",function(a){i()}),j(),a("#remove-local-file").on("change",function(a){j()}),a('.as3cf-setting input[type="text"]').keypress(function(a){if(13===a.which)return a.preventDefault(),!1}),a('input[name="cloudfront"]').on("keyup",function(b){e(a(this))}),a('input[name="domain"]').on("change",function(b){var c=a(this),d=a("#"+k.attr("id")+' form button[type="submit"]');"cloudfront"!==c.val()?d.attr("disabled",!1):e(c.next(".as3cf-setting").find('input[name="cloudfront"]'))}),a('input[name="object-prefix"]').on("change",function(a){f()}),a("#tab-media > .as3cf-bucket-error").detach().insertAfter(".as3cf-bucket-container h3"),a("body").on("click",".bucket-action-manual",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-manual").show().siblings().hide()}),a("body").on("click",".bucket-action-browse",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-select").show().siblings().hide(),as3cf.buckets.loadList(n),n=!1}),a("body").on("click",".bucket-action-create",function(c){c.preventDefault(),a(".as3cf-bucket-name").val(""),a(".as3cf-invalid-bucket-name").html(""),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-create").show().siblings().hide()}),a("body").on("click",".bucket-action-cancel",function(a){a.preventDefault(),as3cf.buckets.resetModal()}),a("body").on("click",".bucket-action-save",function(a){a.preventDefault(),as3cf.buckets.saveManual()}),a("body").on("click",'.as3cf-create-bucket-form button[type="submit"]',function(a){a.preventDefault(),as3cf.buckets.create()}),a("body").on("click",".bucket-action-refresh",function(a){a.preventDefault(),as3cf.buckets.loadList(!0)}),a("body").on("click",".as3cf-bucket-list a",function(b){b.preventDefault(),as3cf.buckets.saveSelected(a(this))}),a(".as3cf-bucket-container").on("click","a.js-link",function(b){return b.preventDefault(),window.open(a(this).attr("href")),!1}),a("body").on("as3cf-modal-open",function(c,d){if(".as3cf-bucket-container."+b.prefix===d){as3cf.buckets.resetModal();var e=a(".as3cf-bucket-manual h3").data("modal-title");a(".as3cf-bucket-manual h3").text(e),as3cf.buckets.disabledButtons()}}),as3cf.buckets.disabledButtons(),a("body").on("input keyup",".as3cf-create-bucket-form .as3cf-bucket-name",function(c){var d=a(this).val(),e=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(d)?e.find("button[type=submit]").removeAttr("disabled"):e.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(d)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(c){var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");d.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
1
+ !function(a,b){function c(b){return a("#"+b+" .as3cf-main-settings form").find("input:not(.no-compare)").serialize()}function d(a){var b=k.find("#"+a),c=b.find("input[type=checkbox]");b.toggleClass("on").find("span").toggleClass("checked");var d=b.find("span.on").hasClass("checked");c.attr("checked",d).trigger("change")}function e(b){var c=b.next(".as3cf-validation-error"),d=a("#"+k.attr("id")+' form button[type="submit"]'),e=/[^a-zA-Z0-9\.\-]/;e.test(b.val())?(c.show(),d.attr("disabled",!0)):(c.hide(),d.attr("disabled",!1))}function f(){var c=a("#"+b.prefix+"-bucket").val(),d=k.find('input[name="object-prefix"]'),e=d.val();""!==e&&(e="&prefix="+encodeURIComponent(e));var f=as3cf.aws_bucket_link+c+e;a("#"+b.prefix+"-view-bucket").attr("href",f)}function g(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf.nonces.get_url_preview};a.each(a("#tab-"+as3cf.tabs.defaultTab+" .as3cf-main-settings form").serializeArray(),function(c,d){var e=d.name,f=d.value;e=e.replace("[]",""),b[e]=void 0===b[e]?f:a.isArray(b[e])?b[e].concat(f):[b[e],f]}),b.action="as3cf-get-url-preview",a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){alert(as3cf.strings.get_url_preview_error+c)},success:function(b,c,d){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function h(a){as3cf.buckets.bucketSelectLock=!1}function i(){a("#remove-local-file").is(":checked")&&a("#serve-from-s3").is(":not(:checked)")?a("#as3cf-lost-files-notice").show():a("#as3cf-lost-files-notice").hide()}function j(){a("#remove-local-file").is(":checked")?a("#as3cf-remove-local-notice").show():a("#as3cf-remove-local-notice").hide()}var k,l={},m=/[^a-z0-9.-]/,n=!1,o=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){c=as3cf.tabs.sanitizeHash(c),o.hide(),k=a("#tab-"+c),k.show(),a(".nav-tab").removeClass("nav-tab-active"),a('a.nav-tab[data-tab="'+c+'"]').addClass("nav-tab-active"),a(".aws-main").attr("data-tab",c),k.attr("data-prefix")&&(b.prefix=k.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show"),"support"===c&&as3cf.tabs.getDiagnosticInfo()},getDiagnosticInfo:function(){var b=a(".debug-log-textarea");b.html(as3cf.strings.get_diagnostic_info);var c={action:"as3cf-get-diagnostic-info",_nonce:as3cf.nonces.get_diagnostic_info};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:c,error:function(a,c,d){b.html(d)},success:function(a,c,d){"undefined"!=typeof a.success?b.html(a.diagnostic_info):(b.html(as3cf.strings.get_diagnostic_info_error),b.append(a.error))}})},sanitizeHash:function(b){var c=a("#tab-"+b);return 0===c.length&&(b=as3cf.tabs.defaultTab),b}},as3cf.buckets={validLength:3,bucketSelectLock:!1,loadList:function(c){"undefined"==typeof c&&(c=!1);var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-list"),e=a("#"+b.prefix+"-bucket").val();if(!1===c&&d.find("li").length>1)return a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"),void this.scrollToSelected();d.html('<li class="loading">'+d.attr("data-working")+"</li>");var f={action:b.prefix+"-get-buckets",_nonce:window[b.prefix.replace(/-/g,"_")].nonces.get_buckets},g=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:f,error:function(a,b,c){d.html(""),g.showError(as3cf.strings.get_buckets_error,c,"as3cf-bucket-select")},success:function(b,c,f){d.html(""),"undefined"!=typeof b.success?(a(".as3cf-bucket-error").hide(),a(b.buckets).each(function(a,b){var c=b.Name===e?"selected":"";d.append('<li><a class="'+c+'" href="#" data-bucket="'+b.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+b.Name+'</span><span class="spinner"></span></span></a></li>')}),g.scrollToSelected()):g.showError(as3cf.strings.get_buckets_error,b.error,"as3cf-bucket-select")}})},scrollToSelected:function(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a(".as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}},resetModal:function(){var c=a(".as3cf-bucket-container."+b.prefix);!1===k.hasClass("as3cf-has-bucket")||"manual"===a("#"+b.prefix+"-bucket-select").val()?(c.find(".as3cf-bucket-manual").show().siblings().hide(),c.find(".bucket-actions.manual").show().siblings(".bucket-actions").hide()):(c.find(".as3cf-bucket-select").show().siblings().hide(),c.find(".bucket-actions.select").show().siblings(".bucket-actions").hide(),this.loadList(n),n=!1),c.find(".as3cf-bucket-error").hide();var d=a("#"+b.prefix+"-bucket").val();c.find(".as3cf-bucket-manual .as3cf-bucket-name").val(d),this.bucketSelectLock=!1},saveManual:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find("button[type=submit]"),f=d.val(),g=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),k.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var h={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(a,b,c){e.text(g),i.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c,d,h){e.text(g),e.prop("disabled",!1),"undefined"!=typeof c.success?(i.set(f,c.region,c.can_write),a("#"+b.prefix+"-bucket-select").val("manual"),a(".as3cf-bucket-list a").removeClass("selected").filter('[data-bucket="'+f+'"]').addClass("selected"),n=!0):i.showError(as3cf.strings.save_bucket_error,c.error,"as3cf-bucket-manual")}})},saveSelected:function(c){var d=a(".as3cf-bucket-list");if(!this.bucketSelectLock){if(this.bucketSelectLock=!0,c.hasClass("selected"))return k.addClass("as3cf-has-bucket"),void b.close();var e=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),c.addClass("selected"),d.addClass("saving"),c.find(".spinner").show().css("visibility","visible");var f=c.attr("data-bucket"),g={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},h=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(b,c,f){d.removeClass("saving"),h.showError(as3cf.strings.save_bucket_error,f,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected")},success:function(g,i,j){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(h.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(h.showError(as3cf.strings.save_bucket_error,g.error,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"))}})}},disabledButtons:function(){if(0!==a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form").length){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").attr("disabled",!1),d.find(".as3cf-bucket-name").val().length<3?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").attr("disabled",!1)}},showError:function(b,c,d){var e=a(".as3cf-bucket-container").children(":visible"),f=e.find(".as3cf-bucket-error");d="undefined"==typeof d?null:d,d&&!e.hasClass(d)||(f.find("span.title").html(b+" &mdash;"),f.find("span.message").html(c),f.show(),this.bucketSelectLock=!1)},set:function(e,i,j){var m=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),n=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&0===n.text().trim().length){d("copy-to-s3-wrap"),d("serve-from-s3-wrap");var o=k.attr("id");l[o]=c(o)}a(".as3cf-error.fatal").hide(),n.text(e),m.find(".as3cf-bucket-name").val(e),a("#"+b.prefix+"-bucket").val(e),a("#"+b.prefix+"-region").val(i),a(".updated").not(".as3cf-notice").show(),k.addClass("as3cf-has-bucket"),k.find(".as3cf-can-write-error").toggle(!j),k.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&g(),f(),b.close(h)},create:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find(".bucket-create-region"),f=c.find("button[type=submit]"),g=d.val(),h=f.text();a(".as3cf-bucket-error").hide(),f.text(f.attr("data-working")),f.prop("disabled",!0);var i={action:b.prefix+"-create-bucket",bucket_name:g,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.create_bucket};e.val()&&(i.region=e.val());var j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){f.text(h),j.showError(as3cf.strings.create_bucket_error,c,"as3cf-bucket-create")},success:function(b,c,e){f.text(h),f.prop("disabled",!1),"undefined"!=typeof b.success?(j.set(g,b.region,b.can_write),a(".as3cf-bucket-select-region").hide(),a(".as3cf-bucket-select-region").removeAttr("selected"),d.val(""),f.attr("disabled",!0),n=!0):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return a.length<3||a.length>63?!1:!0!==m.test(a)},updateNameNotice:function(b){var c=null;!0===m.test(b)?c=as3cf.strings.create_bucket_invalid_chars:b.length<3?c=as3cf.strings.create_bucket_name_short:b.length>63&&(c=as3cf.strings.create_bucket_name_long),c&&b.length>0?a(".as3cf-invalid-bucket-name").html(c):a(".as3cf-invalid-bucket-name").html("")}},a(document).ready(function(){var h=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(h),window.location.hash){var m=window.location.hash.substring(1);as3cf.tabs.toggle(m,!0)}else k=a("#tab-"+as3cf.tabs.defaultTab),a(".aws-main").attr("data-tab",as3cf.tabs.defaultTab);a(".aws-main").on("click",".nav-tab",function(b){if(b.preventDefault(),!a(this).hasClass("nav-tab-active")){var c=a(this).attr("data-tab");as3cf.tabs.toggle(c),"media"===c?(window.location.hash="","function"==typeof window.history.replaceState&&"#"===window.location.href.slice(-1)&&history.replaceState({},"",window.location.href.slice(0,-1))):window.location.hash=c}}),o.length&&o.each(function(a,b){l[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(l)){var b=k.attr("id");return c(b)!==l[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(b){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(b){a(this).hasClass("disabled")||d(a(this).attr("id"))}),o.on("change",".sub-toggle",function(b){var c=a(this).attr("id");a(".as3cf-setting."+c).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(b){var c=a(this).closest('input:radio[name="domain"]:checked'),d=c.val(),e=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),f="cloudfront"===d;e.toggleClass("hide",!f)}),a(".url-preview").on("change","input",function(a){g()}),i(),a("#serve-from-s3,#remove-local-file").on("change",function(a){i()}),j(),a("#remove-local-file").on("change",function(a){j()}),a('.as3cf-setting input[type="text"]').keypress(function(a){return 13===a.which?(a.preventDefault(),!1):void 0}),a('input[name="cloudfront"]').on("keyup",function(b){e(a(this))}),a('input[name="domain"]').on("change",function(b){var c=a(this),d=a("#"+k.attr("id")+' form button[type="submit"]');"cloudfront"!==c.val()?d.attr("disabled",!1):e(c.next(".as3cf-setting").find('input[name="cloudfront"]'))}),a('input[name="object-prefix"]').on("change",function(a){f()}),a("#tab-media > .as3cf-bucket-error").detach().insertAfter(".as3cf-bucket-container h3"),a("body").on("click",".bucket-action-manual",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-manual").show().siblings().hide()}),a("body").on("click",".bucket-action-browse",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-select").show().siblings().hide(),as3cf.buckets.loadList(n),n=!1}),a("body").on("click",".bucket-action-create",function(c){c.preventDefault(),a(".as3cf-bucket-name").val(""),a(".as3cf-invalid-bucket-name").html(""),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-create").show().siblings().hide()}),a("body").on("click",".bucket-action-cancel",function(a){a.preventDefault(),as3cf.buckets.resetModal()}),a("body").on("click",".bucket-action-save",function(a){a.preventDefault(),as3cf.buckets.saveManual()}),a("body").on("click",'.as3cf-create-bucket-form button[type="submit"]',function(a){a.preventDefault(),as3cf.buckets.create()}),a("body").on("click",".bucket-action-refresh",function(a){a.preventDefault(),as3cf.buckets.loadList(!0)}),a("body").on("click",".as3cf-bucket-list a",function(b){b.preventDefault(),as3cf.buckets.saveSelected(a(this))}),a(".as3cf-bucket-container").on("click","a.js-link",function(b){return b.preventDefault(),window.open(a(this).attr("href")),!1}),a("body").on("as3cf-modal-open",function(c,d){if(".as3cf-bucket-container."+b.prefix===d){as3cf.buckets.resetModal();var e=a(".as3cf-bucket-manual h3").data("modal-title");a(".as3cf-bucket-manual h3").text(e),as3cf.buckets.disabledButtons()}}),as3cf.buckets.disabledButtons(),a("body").on("input keyup",".as3cf-create-bucket-form .as3cf-bucket-name",function(c){var d=a(this).val(),e=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(d)?e.find("button[type=submit]").removeAttr("disabled"):e.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(d)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(c){var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");d.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
assets/sass/attachment.scss ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #s3-actions.postbox {
2
+ .inside {
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+ a, a:hover {
7
+ text-decoration: none;
8
+ }
9
+ .s3-details {
10
+ padding: 6px 0;
11
+
12
+ .misc-pub-section {
13
+ clear: both;
14
+ float: left;
15
+ width: 100%;
16
+ -webkit-box-sizing: border-box;
17
+ -moz-box-sizing: border-box;
18
+ box-sizing: border-box;
19
+
20
+ .s3-key {
21
+ float: left;
22
+ width: 20%;
23
+ }
24
+ .s3-value {
25
+ font-weight: bold;
26
+ float: left;
27
+ width: 80%;
28
+ }
29
+ }
30
+ .not-copied {
31
+ color: #666666;
32
+ }
33
+ }
34
+ .s3-actions {
35
+ padding: 10px;
36
+ clear: both;
37
+ border-top: 1px solid #ddd;
38
+ border-bottom: 1px solid #ddd;
39
+ background: #f5f5f5;
40
+
41
+ .copy-action {
42
+ text-align: right;
43
+ float: right;
44
+ line-height: 23px;
45
+ }
46
+
47
+ .remove-action {
48
+ line-height: 28px;
49
+ vertical-align: middle;
50
+ text-align: left;
51
+ float: left;
52
+
53
+ a.local-warning {
54
+ color: #a00;
55
+ &:hover {
56
+ color: #f00;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
assets/sass/media.scss ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body.as3cf-pro {
2
+ .attachments-browser .media-toolbar-secondary {
3
+ max-width: 100%;
4
+ }
5
+ }
6
+
7
+ .as3cfpro_remove a.local-warning {
8
+ color: #a00;
9
+ &:hover {
10
+ color: #f00;
11
+ text-decoration: none;
12
+ border: none;
13
+ }
14
+ }
15
+
16
+ .media-modal a.local-warning {
17
+ color: #bc0b0b;
18
+ &:hover {
19
+ color: red;
20
+ }
21
+ }
22
+
23
+ .attachment-info .attachment-s3-details {
24
+ font-weight: bold;
25
+ margin-bottom: 5px;
26
+ }
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -42,6 +42,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
42
  */
43
  protected $default_tab = '';
44
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * @var AS3CF_Notices
47
  */
@@ -57,11 +67,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
57
  */
58
  protected static $buckets_check = array();
59
 
60
- /**
61
- * @var array
62
- */
63
- protected $encode_files = array();
64
-
65
  /**
66
  * @var AS3CF_Plugin_Compatibility
67
  */
@@ -75,6 +80,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
75
  const SETTINGS_KEY = 'tantan_wordpress_s3';
76
  const SETTINGS_CONSTANT = 'WPOS3_SETTINGS';
77
 
 
 
78
  /**
79
  * @param string $plugin_file_path
80
  * @param Amazon_Web_Services $aws
@@ -86,7 +93,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
86
  parent::__construct( $plugin_file_path );
87
 
88
  $this->aws = $aws;
89
- $this->notices = AS3CF_Notices::get_instance( $this, $plugin_file_path );
90
 
91
  $this->init( $plugin_file_path );
92
  }
@@ -104,7 +111,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
104
  new AS3CF_Upgrade_Region_Meta( $this );
105
  new AS3CF_Upgrade_File_Sizes( $this );
106
  new AS3CF_Upgrade_Meta_WP_Error( $this );
107
- $this->maybe_display_deprecated_retina_notice();
 
108
 
109
  // Plugin setup
110
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
@@ -113,12 +121,18 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
113
  // display a notice when either lite or pro is automatically deactivated
114
  add_action( 'pre_current_active_plugins', array( $this, 'plugin_deactivated_notice' ) );
115
 
 
 
 
 
 
116
  // UI AJAX
117
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
118
  add_action( 'wp_ajax_as3cf-save-bucket', array( $this, 'ajax_save_bucket' ) );
119
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
120
  add_action( 'wp_ajax_as3cf-manual-save-bucket', array( $this, 'ajax_save_bucket' ) );
121
  add_action( 'wp_ajax_as3cf-get-url-preview', array( $this, 'ajax_get_url_preview' ) );
 
122
  add_action( 'wp_ajax_as3cf-get-diagnostic-info', array( $this, 'ajax_get_diagnostic_info' ) );
123
 
124
  // Rewriting URLs, doesn't depend on plugin being setup
@@ -135,6 +149,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
135
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
136
  add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
137
 
 
 
 
 
138
  // include compatibility code for other plugins
139
  $this->plugin_compat = new AS3CF_Plugin_Compatibility( $this );
140
 
@@ -229,6 +247,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
229
  return '1';
230
  }
231
 
 
 
 
 
 
 
 
 
 
 
232
  // Turn on object versioning by default
233
  if ( 'object-versioning' == $key && ! isset( $settings['object-versioning'] ) ) {
234
  return '1';
@@ -589,6 +617,26 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
589
  }
590
  }
591
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
  /**
593
  * Generate a preview of the URL of files uploaded to S3
594
  *
@@ -610,7 +658,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
610
 
611
  $url = $scheme . '://' . $domain . '/' . $path . $suffix;
612
 
613
- // replace hyphens with non breaking hyphens for formatting
614
  if ( $escape ) {
615
  $url = str_replace( '-', '&#8209;', $url );
616
  }
@@ -694,7 +742,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
694
  * to cope with possible different regions
695
  */
696
  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 ) {
697
- $prefix = trailingslashit( dirname( $s3object['key'] ) );
698
  $bucket = $s3object['bucket'];
699
  $region = $this->get_s3object_region( $s3object );
700
  $paths = $this->get_attachment_file_paths( $post_id, false, false, $remove_backup_sizes );
@@ -836,8 +884,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
836
  $acl = $old_s3object['acl'];
837
  }
838
  // use existing prefix
839
- $prefix = dirname( $old_s3object['key'] );
840
- $prefix = ( '.' === $prefix ) ? '' : $prefix . '/';
841
  // use existing bucket
842
  $bucket = $old_s3object['bucket'];
843
  // get existing region
@@ -904,9 +951,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
904
  $files_to_remove = array();
905
 
906
  if ( file_exists( $file_path ) ) {
907
- $files_to_remove[] = $file_path;
908
  try {
909
  $s3client->putObject( $args );
 
910
  } catch ( Exception $e ) {
911
  $error_msg = sprintf( __( 'Error uploading %s to S3: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
912
 
@@ -940,14 +987,21 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
940
  }
941
  }
942
 
943
- foreach ( $file_paths as $file_path ) {
 
 
944
  if ( ! in_array( $file_path, $files_to_remove ) ) {
 
 
945
  $additional_images[] = array(
946
  'Key' => $prefix . basename( $file_path ),
947
  'SourceFile' => $file_path,
 
948
  );
949
 
950
- $files_to_remove[] = $file_path;
 
 
951
 
952
  if ( $remove_local_files_setting ) {
953
  // Record the file size for the additional image
@@ -961,9 +1015,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
961
 
962
  foreach ( $additional_images as $image ) {
963
  try {
964
- $args = array_merge( $args, $image );
965
- $args['ACL'] = self::DEFAULT_ACL;
966
  $s3client->putObject( $args );
 
967
  } catch ( Exception $e ) {
968
  AS3CF_Error::log( 'Error uploading ' . $args['SourceFile'] . ' to S3: ' . $e->getMessage() );
969
  }
@@ -975,7 +1029,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
975
  $files_to_remove = apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $post_id, $file_path );
976
  // Remove duplicates
977
  $files_to_remove = array_unique( $files_to_remove );
978
-
979
  // Delete the files
980
  $this->remove_local_files( $files_to_remove );
981
  }
@@ -1001,6 +1054,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1001
  }
1002
  }
1003
 
 
 
 
 
 
 
1004
  do_action( 'wpos3_post_upload_attachment', $post_id, $s3object );
1005
 
1006
  if ( ! is_null( $return_metadata ) ) {
@@ -1337,26 +1396,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1337
  return $filename;
1338
  }
1339
 
1340
- /**
1341
- * Get attachment url
1342
- *
1343
- * @param string $url
1344
- * @param int $post_id
1345
- *
1346
- * @return bool|mixed|void|WP_Error
1347
- */
1348
- function wp_get_attachment_url( $url, $post_id ) {
1349
- $new_url = $this->get_attachment_url( $post_id );
1350
- if ( false === $new_url ) {
1351
- return $url;
1352
- }
1353
-
1354
- $new_url = apply_filters( 'wps3_get_attachment_url', $new_url, $post_id, $this ); // Old naming convention, will be deprecated soon
1355
- $new_url = apply_filters( 'as3cf_wp_get_attachment_url', $new_url, $post_id );
1356
-
1357
- return $new_url;
1358
- }
1359
-
1360
  /**
1361
  * Get attachment s3 info
1362
  *
@@ -1364,7 +1403,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1364
  *
1365
  * @return mixed
1366
  */
1367
- function get_attachment_s3_info( $post_id ) {
1368
  return apply_filters( 'as3cf_get_attachment_s3_info', get_post_meta( $post_id, 'amazonS3_info', true ), $post_id );
1369
  }
1370
 
@@ -1570,25 +1609,101 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1570
  *
1571
  * @return bool|mixed|void|WP_Error
1572
  */
1573
- function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null, $headers = array(), $skip_rewrite_check = false ) {
1574
  if ( ! ( $s3object = $this->is_attachment_served_by_s3( $post_id, $skip_rewrite_check ) ) ) {
1575
  return false;
1576
  }
1577
 
1578
- return $this->get_attachment_s3_url( $post_id, $s3object, $expires, $size, $meta, $headers );
 
 
1579
  }
1580
 
1581
  /**
1582
- * Get the S3 URL for an attachment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1583
  *
1584
  * @param int $post_id
1585
- * @param array $s3object
1586
- * @param null|int $expires
1587
- * @param null|string $size
1588
- * @param null|array $meta
1589
- * @param array $headers
1590
  *
1591
- * @return mixed|void|WP_Error
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1592
  */
1593
  public function get_attachment_s3_url( $post_id, $s3object, $expires = null, $size = null, $meta = null, $headers = array() ) {
1594
  $scheme = $this->get_s3_url_scheme();
@@ -1601,12 +1716,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1601
  $region = '';
1602
  }
1603
 
1604
- // force use of secured url when ACL has been set to private
1605
- if ( is_null( $expires ) && isset( $s3object['acl'] ) && self::PRIVATE_ACL == $s3object['acl'] ) {
1606
- $expires = self::DEFAULT_EXPIRES;
1607
- }
1608
 
1609
- $domain_bucket = $this->get_s3_url_domain( $s3object['bucket'], $region, $expires );
 
 
 
 
 
 
 
 
 
 
 
1610
 
1611
  if ( ! is_null( $size ) ) {
1612
  if ( is_null( $meta ) ) {
@@ -1617,7 +1740,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1617
  return $meta;
1618
  }
1619
 
1620
- if ( isset( $meta['sizes'][ $size ]['file'] ) ) {
1621
  $size_prefix = dirname( $s3object['key'] );
1622
  $size_file_prefix = ( '.' === $size_prefix ) ? '' : $size_prefix . '/';
1623
 
@@ -1638,12 +1761,34 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1638
 
1639
  $s3object['key'] = $this->maybe_update_cloudfront_path( $s3object['key'] );
1640
 
1641
- $file = $this->encode_filename_in_path( $s3object['key'], $post_id );
1642
- $url = $scheme . '://' . $domain_bucket . '/' . $file;
 
1643
 
1644
  return apply_filters( 'as3cf_get_attachment_url', $url, $s3object, $post_id, $expires, $headers );
1645
  }
1646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1647
  /**
1648
  * Maybe encode attachment URLs when retrieving the image tag
1649
  *
@@ -1657,7 +1802,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1657
  * @return string
1658
  */
1659
  public function maybe_encode_get_image_tag( $html, $id, $alt, $title, $align, $size ) {
1660
- if ( ! $this->is_attachment_served_by_s3( $id ) ) {
 
1661
  return $html;
1662
  }
1663
 
@@ -1669,9 +1815,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1669
  }
1670
 
1671
  $img_src = $matches[1];
1672
- $encoded_src = $this->encode_filename_in_path( $img_src, $id );
 
1673
 
1674
- return str_replace( $img_src, $encoded_src, $html );
1675
  }
1676
 
1677
  /**
@@ -1685,12 +1832,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1685
  * @return array
1686
  */
1687
  public function maybe_encode_wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
1688
- if ( ! $this->is_attachment_served_by_s3( $attachment_id ) ) {
 
1689
  return $image;
1690
  }
1691
 
1692
  if ( isset( $image[0] ) ) {
1693
- $image[0] = $this->encode_filename_in_path( $image[0], $attachment_id );
 
 
 
1694
  }
1695
 
1696
  return $image;
@@ -1706,17 +1857,21 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1706
  * @return array
1707
  */
1708
  public function maybe_encode_wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
1709
- if ( ! $this->is_attachment_served_by_s3( $attachment->ID ) ) {
 
1710
  return $response;
1711
  }
1712
 
1713
  if ( isset( $response['url'] ) ) {
1714
- $response['url'] = $this->encode_filename_in_path( $response['url'], $attachment->ID );
1715
  }
1716
 
1717
  if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
1718
- foreach ( $response['sizes'] as $key => $value ) {
1719
- $response['sizes'][ $key ]['url'] = $this->encode_filename_in_path( $value['url'], $attachment->ID );
 
 
 
1720
  }
1721
  }
1722
 
@@ -1733,17 +1888,110 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1733
  * @return array
1734
  */
1735
  public function maybe_encode_image_get_intermediate_size( $data, $post_id, $size ) {
1736
- if ( ! $this->is_attachment_served_by_s3( $post_id ) ) {
 
1737
  return $data;
1738
  }
1739
 
1740
  if ( isset( $data['url'] ) ) {
1741
- $data['url'] = $this->encode_filename_in_path( $data['url'], $post_id );
 
 
 
1742
  }
1743
 
1744
  return $data;
1745
  }
1746
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1747
  /**
1748
  * Is attachment served by S3.
1749
  *
@@ -1770,12 +2018,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1770
  * Encode file names according to RFC 3986 when generating urls
1771
  * As per Amazon https://forums.aws.amazon.com/thread.jspa?threadID=55746#jive-message-244233
1772
  *
1773
- * @param string $file
1774
- * @param null|int $attachment_id
1775
  *
1776
- * @return string Encoded filename with path prefix untouched
1777
  */
1778
- function encode_filename_in_path( $file, $attachment_id = null ) {
1779
  $url = parse_url( $file );
1780
 
1781
  if ( ! isset( $url['path'] ) ) {
@@ -1783,61 +2030,48 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1783
  return $file;
1784
  }
1785
 
1786
- if ( in_array( $this->normalize_file_path( $url['path'], $attachment_id ), $this->encode_files ) ) {
1787
- // Already encoded, return original
 
 
1788
  return $file;
1789
  }
1790
 
1791
- $file_path = dirname( $file );
1792
- $file_path = ( '.' !== $file_path ) ? trailingslashit( $file_path ) : '';
1793
- $file_name = basename( $url['path'] );
1794
  $encoded_file_name = rawurlencode( $file_name );
1795
- $encoded_file_path = $file_path . $encoded_file_name;
1796
 
1797
  if ( $file_name === $encoded_file_name ) {
1798
  // File name doesn't need encoding, return original
1799
  return $file;
1800
  }
1801
 
1802
- $normalized_file_path = $this->normalize_file_path( $encoded_file_path, $attachment_id );
1803
-
1804
- if ( ! in_array( $normalized_file_path, $this->encode_files ) ) {
1805
- $this->encode_files[] = $normalized_file_path;
1806
- }
1807
-
1808
  return str_replace( $file_name, $encoded_file_name, $file );
1809
  }
1810
 
1811
  /**
1812
- * Normalize file path
1813
  *
1814
- * @param string $path
1815
- * @param null|int $attachment_id
1816
  *
1817
- * @return string mixed
1818
  */
1819
- public function normalize_file_path( $path, $attachment_id = null ) {
1820
- $url = parse_url( $path );
1821
 
1822
- if ( isset( $url['scheme'] ) ) {
1823
- $path = str_replace( $url['scheme'] . '://', '', $path );
1824
- } else {
1825
- $path = ltrim( $path, '/' );
1826
 
1827
- if ( ! is_null( $attachment_id ) ) {
1828
- // Attempt to remove bucket from path using amazonS3_info key
1829
- $s3info = $this->get_attachment_s3_info( $attachment_id );
1830
- $bucket = $s3info['bucket'];
1831
- } else {
1832
- // Attempt to remove bucket from path using tantan key
1833
- $bucket = $this->get_setting( 'bucket' );
1834
- }
1835
 
1836
- $preg = '/^' . preg_quote( $bucket ) . '/';
1837
- $path = preg_replace( $preg, '', $path );
 
1838
  }
1839
 
1840
- return '/' . ltrim( $path, '/' );
 
 
1841
  }
1842
 
1843
  /**
@@ -2783,6 +3017,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2783
  if ( $acl == self::DEFAULT_ACL ) {
2784
  unset( $s3object['acl'] );
2785
  }
 
2786
  update_post_meta( $post_id, 'amazonS3_info', $s3object );
2787
  } catch ( Exception $e ) {
2788
  $msg = 'Error setting ACL to ' . $acl . ' for ' . $s3object['key'] . ': ' . $e->getMessage();
@@ -3132,7 +3367,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3132
  $output .= "\r\n";
3133
  $output .= "\r\n";
3134
 
3135
- $output .= 'URL Preview: ';
 
 
 
3136
  $output .= $this->get_url_preview( $escape );
3137
  $output .= "\r\n";
3138
  $output .= "\r\n";
@@ -3433,13 +3671,13 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3433
  }
3434
 
3435
  // Original file
3436
- $paths[] = $original_file;
3437
 
3438
  // Sizes
3439
  if ( isset( $meta['sizes'] ) ) {
3440
- foreach ( $meta['sizes'] as $size ) {
3441
- if ( isset( $size['file'] ) ) {
3442
- $paths[] = str_replace( $file_name, $size['file'], $file_path );
3443
  }
3444
  }
3445
  }
@@ -3578,7 +3816,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3578
  * true - Attachments only uploaded to S3
3579
  * false - Attachments not uploaded to S3
3580
  *
3581
- * @return null|string
3582
  */
3583
  public function count_attachments( $prefix, $uploaded_to_s3 = null ) {
3584
  global $wpdb;
@@ -3599,7 +3837,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3599
 
3600
  $sql .= ' ' . $where;
3601
 
3602
- return $wpdb->get_var( $sql );
3603
  }
3604
 
3605
  /**
@@ -3737,11 +3975,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3737
  * @param string $variable
3738
  * @param int $type
3739
  * @param int $filter
 
3740
  *
3741
  * @return mixed
3742
  */
3743
- public function filter_input( $variable, $type = INPUT_GET, $filter = FILTER_DEFAULT ) {
3744
- return filter_input( $type, $variable, $filter );
3745
  }
3746
 
3747
  /**
@@ -3790,31 +4029,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3790
  return $domain;
3791
  }
3792
 
3793
- /**
3794
- * Display a notice if using the retina hidpi setting when
3795
- * we removed support for it in 1.1.
3796
- */
3797
- protected function maybe_display_deprecated_retina_notice() {
3798
- if ( ! $this->get_setting( 'hidpi-images' ) ) {
3799
- // Not using setting, abort
3800
- return;
3801
- }
3802
-
3803
- $notice_args = array(
3804
- 'type' => 'notice-info',
3805
- 'only_show_to_user' => false,
3806
- 'flash' => false,
3807
- );
3808
-
3809
- $doc_url = 'https://deliciousbrains.com/wp-offload-s3/doc/copy-hidpi-2x-images-support/';
3810
- $doc_link = $this->dbrains_link( $doc_url, __( 'this doc' ) );
3811
-
3812
- $message = sprintf( '<strong>%s</strong> &mdash; ', __( 'WP Offload S3 Feature Removed', 'amazon-s3-and-cloudfront' ) );
3813
- $message .= sprintf( __( 'The "Copy HiDPI (@2x) Images" feature has been removed as of version 1.1 of WP Offload S3. It looks like you had this feature turned on. Please see %s for why we removed this feature and how you can continue copying @2x images to S3.', 'amazon-s3-and-cloudfront' ), $doc_link );
3814
-
3815
- $this->notices->add_notice( $message, $notice_args );
3816
- }
3817
-
3818
  /**
3819
  * Display a notice if using setting to force HTTP as url scheme, removed in 1.3.
3820
  */
@@ -3856,4 +4070,268 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3856
 
3857
  return $path;
3858
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3859
  }
42
  */
43
  protected $default_tab = '';
44
 
45
+ /**
46
+ * @var AS3CF_Local_To_S3
47
+ */
48
+ public $filter_local;
49
+
50
+ /**
51
+ * @var AS3CF_S3_To_Local
52
+ */
53
+ public $filter_s3;
54
+
55
  /**
56
  * @var AS3CF_Notices
57
  */
67
  */
68
  protected static $buckets_check = array();
69
 
 
 
 
 
 
70
  /**
71
  * @var AS3CF_Plugin_Compatibility
72
  */
80
  const SETTINGS_KEY = 'tantan_wordpress_s3';
81
  const SETTINGS_CONSTANT = 'WPOS3_SETTINGS';
82
 
83
+ const LATEST_UPGRADE_ROUTINE = 5;
84
+
85
  /**
86
  * @param string $plugin_file_path
87
  * @param Amazon_Web_Services $aws
93
  parent::__construct( $plugin_file_path );
94
 
95
  $this->aws = $aws;
96
+ $this->notices = AS3CF_Notices::get_instance( $this );
97
 
98
  $this->init( $plugin_file_path );
99
  }
111
  new AS3CF_Upgrade_Region_Meta( $this );
112
  new AS3CF_Upgrade_File_Sizes( $this );
113
  new AS3CF_Upgrade_Meta_WP_Error( $this );
114
+ new AS3CF_Upgrade_Content_Replace_URLs( $this );
115
+ new AS3CF_Upgrade_EDD_Replace_URLs( $this );
116
 
117
  // Plugin setup
118
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
121
  // display a notice when either lite or pro is automatically deactivated
122
  add_action( 'pre_current_active_plugins', array( $this, 'plugin_deactivated_notice' ) );
123
 
124
+ // Attachment screens/modals
125
+ add_action( 'load-upload.php', array( $this, 'load_media_assets' ), 11 );
126
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_attachment_assets' ), 11 );
127
+ add_action( 'add_meta_boxes', array( $this, 'attachment_s3_meta_box' ) );
128
+
129
  // UI AJAX
130
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
131
  add_action( 'wp_ajax_as3cf-save-bucket', array( $this, 'ajax_save_bucket' ) );
132
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
133
  add_action( 'wp_ajax_as3cf-manual-save-bucket', array( $this, 'ajax_save_bucket' ) );
134
  add_action( 'wp_ajax_as3cf-get-url-preview', array( $this, 'ajax_get_url_preview' ) );
135
+ add_action( 'wp_ajax_as3cf_get_attachment_s3_details', array( $this, 'ajax_get_attachment_s3_details' ) );
136
  add_action( 'wp_ajax_as3cf-get-diagnostic-info', array( $this, 'ajax_get_diagnostic_info' ) );
137
 
138
  // Rewriting URLs, doesn't depend on plugin being setup
149
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
150
  add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
151
 
152
+ // Content filtering
153
+ $this->filter_local = new AS3CF_Local_To_S3( $this );
154
+ $this->filter_s3 = new AS3CF_S3_To_Local( $this );
155
+
156
  // include compatibility code for other plugins
157
  $this->plugin_compat = new AS3CF_Plugin_Compatibility( $this );
158
 
247
  return '1';
248
  }
249
 
250
+ // Don't run upgrade routines on fresh install
251
+ if ( 'post_meta_version' === $key && ! isset( $settings['post_meta_version'] ) ) {
252
+ $routine = self::LATEST_UPGRADE_ROUTINE;
253
+
254
+ $this->set_setting( 'post_meta_version', $routine );
255
+ $this->save_settings();
256
+
257
+ return $routine;
258
+ }
259
+
260
  // Turn on object versioning by default
261
  if ( 'object-versioning' == $key && ! isset( $settings['object-versioning'] ) ) {
262
  return '1';
617
  }
618
  }
619
 
620
+ /**
621
+ * Get local URL preview.
622
+ *
623
+ * @param bool $escape
624
+ * @param string $suffix
625
+ *
626
+ * @return string
627
+ */
628
+ protected function get_local_url_preview( $escape = true, $suffix = 'photo.jpg' ) {
629
+ $uploads = wp_upload_dir();
630
+ $url = trailingslashit( $uploads['url'] ) . $suffix;
631
+
632
+ // Replace hyphens with non breaking hyphens for formatting
633
+ if ( $escape ) {
634
+ $url = str_replace( '-', '&#8209;', $url );
635
+ }
636
+
637
+ return $url;
638
+ }
639
+
640
  /**
641
  * Generate a preview of the URL of files uploaded to S3
642
  *
658
 
659
  $url = $scheme . '://' . $domain . '/' . $path . $suffix;
660
 
661
+ // Replace hyphens with non breaking hyphens for formatting
662
  if ( $escape ) {
663
  $url = str_replace( '-', '&#8209;', $url );
664
  }
742
  * to cope with possible different regions
743
  */
744
  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 ) {
745
+ $prefix = $this->normalize_object_prefix( $s3object['key'] );
746
  $bucket = $s3object['bucket'];
747
  $region = $this->get_s3object_region( $s3object );
748
  $paths = $this->get_attachment_file_paths( $post_id, false, false, $remove_backup_sizes );
884
  $acl = $old_s3object['acl'];
885
  }
886
  // use existing prefix
887
+ $prefix = $this->normalize_object_prefix( $old_s3object['key'] );
 
888
  // use existing bucket
889
  $bucket = $old_s3object['bucket'];
890
  // get existing region
951
  $files_to_remove = array();
952
 
953
  if ( file_exists( $file_path ) ) {
 
954
  try {
955
  $s3client->putObject( $args );
956
+ $files_to_remove[] = $file_path;
957
  } catch ( Exception $e ) {
958
  $error_msg = sprintf( __( 'Error uploading %s to S3: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
959
 
987
  }
988
  }
989
 
990
+ $s3object_sizes = array();
991
+
992
+ foreach ( $file_paths as $size => $file_path ) {
993
  if ( ! in_array( $file_path, $files_to_remove ) ) {
994
+ $acl = apply_filters( 'as3cf_upload_acl_sizes', self::DEFAULT_ACL, $size, $post_id, $data );
995
+
996
  $additional_images[] = array(
997
  'Key' => $prefix . basename( $file_path ),
998
  'SourceFile' => $file_path,
999
+ 'ACL' => $acl,
1000
  );
1001
 
1002
+ if ( self::DEFAULT_ACL !== $acl ) {
1003
+ $s3object_sizes[ $size ]['acl'] = $acl;
1004
+ }
1005
 
1006
  if ( $remove_local_files_setting ) {
1007
  // Record the file size for the additional image
1015
 
1016
  foreach ( $additional_images as $image ) {
1017
  try {
1018
+ $args = array_merge( $args, $image );
 
1019
  $s3client->putObject( $args );
1020
+ $files_to_remove[] = $image['SourceFile'];
1021
  } catch ( Exception $e ) {
1022
  AS3CF_Error::log( 'Error uploading ' . $args['SourceFile'] . ' to S3: ' . $e->getMessage() );
1023
  }
1029
  $files_to_remove = apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $post_id, $file_path );
1030
  // Remove duplicates
1031
  $files_to_remove = array_unique( $files_to_remove );
 
1032
  // Delete the files
1033
  $this->remove_local_files( $files_to_remove );
1034
  }
1054
  }
1055
  }
1056
 
1057
+ if ( ! empty( $s3object_sizes ) ) {
1058
+ // Additional image sizes have custom ACLs, update meta
1059
+ $s3object['sizes'] = $s3object_sizes;
1060
+ update_post_meta( $post_id, 'amazonS3_info', $s3object );
1061
+ }
1062
+
1063
  do_action( 'wpos3_post_upload_attachment', $post_id, $s3object );
1064
 
1065
  if ( ! is_null( $return_metadata ) ) {
1396
  return $filename;
1397
  }
1398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1399
  /**
1400
  * Get attachment s3 info
1401
  *
1403
  *
1404
  * @return mixed
1405
  */
1406
+ public function get_attachment_s3_info( $post_id ) {
1407
  return apply_filters( 'as3cf_get_attachment_s3_info', get_post_meta( $post_id, 'amazonS3_info', true ), $post_id );
1408
  }
1409
 
1609
  *
1610
  * @return bool|mixed|void|WP_Error
1611
  */
1612
+ public function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null, $headers = array(), $skip_rewrite_check = false ) {
1613
  if ( ! ( $s3object = $this->is_attachment_served_by_s3( $post_id, $skip_rewrite_check ) ) ) {
1614
  return false;
1615
  }
1616
 
1617
+ $url = $this->get_attachment_s3_url( $post_id, $s3object, $expires, $size, $meta, $headers );
1618
+
1619
+ return apply_filters( 'as3cf_wp_get_attachment_url', $url, $post_id );
1620
  }
1621
 
1622
  /**
1623
+ * Get attachment local URL.
1624
+ *
1625
+ * This is a direct copy of wp_get_attachment_url() from /wp-includes/post.php
1626
+ * as we filter the URL in AS3CF and can't remove this filter using the current implementation
1627
+ * of globals for class instances.
1628
+ *
1629
+ * @param int $post_id
1630
+ *
1631
+ * @return string|false Attachment URL, otherwise false.
1632
+ */
1633
+ public function get_attachment_local_url( $post_id ) {
1634
+ $url = '';
1635
+
1636
+ // Get attached file.
1637
+ if ( $file = get_post_meta( $post_id, '_wp_attached_file', true ) ) {
1638
+ // Get upload directory.
1639
+ if ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) {
1640
+ // Check that the upload base exists in the file location.
1641
+ if ( 0 === strpos( $file, $uploads['basedir'] ) ) {
1642
+ // Replace file location with url location.
1643
+ $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file );
1644
+ } elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) {
1645
+ $url = $uploads['baseurl'] . substr( $file, strpos( $file, 'wp-content/uploads' ) + 18 );
1646
+ } else {
1647
+ // It's a newly-uploaded file, therefore $file is relative to the basedir.
1648
+ $url = $uploads['baseurl'] . "/$file";
1649
+ }
1650
+ }
1651
+ }
1652
+
1653
+ if ( empty( $url ) ) {
1654
+ return false;
1655
+ }
1656
+
1657
+ // Set correct domain on multisite subdomain installs
1658
+ if ( is_multisite() ) {
1659
+ $siteurl = trailingslashit( get_option( 'siteurl' ) );
1660
+ $network_siteurl = trailingslashit( network_site_url() );
1661
+
1662
+ if ( 0 !== strpos( $url, $siteurl ) ) {
1663
+ // URL already using site URL, no replacement needed
1664
+ $url = str_replace( $network_siteurl, $siteurl, $url );
1665
+ }
1666
+ }
1667
+
1668
+ return $url;
1669
+ }
1670
+
1671
+ /**
1672
+ * Get attachment local URL size.
1673
  *
1674
  * @param int $post_id
1675
+ * @param string|null $size
 
 
 
 
1676
  *
1677
+ * @return false|string
1678
+ */
1679
+ public function get_attachment_local_url_size( $post_id, $size = null ) {
1680
+ $url = $this->get_attachment_local_url( $post_id );
1681
+
1682
+ if ( empty( $size ) ) {
1683
+ return $url;
1684
+ }
1685
+
1686
+ $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
1687
+
1688
+ if ( empty( $meta['sizes'][ $size ]['file'] ) ) {
1689
+ // No alternative sizes available, return
1690
+ return $url;
1691
+ }
1692
+
1693
+ return str_replace( wp_basename( $url ), $meta['sizes'][ $size ]['file'], $url );
1694
+ }
1695
+
1696
+ /**
1697
+ * Get the S3 URL for an attachment
1698
+ *
1699
+ * @param int $post_id
1700
+ * @param array $s3object
1701
+ * @param null|int $expires
1702
+ * @param null|string|array $size
1703
+ * @param null|array $meta
1704
+ * @param array $headers
1705
+ *
1706
+ * @return mixed|WP_Error
1707
  */
1708
  public function get_attachment_s3_url( $post_id, $s3object, $expires = null, $size = null, $meta = null, $headers = array() ) {
1709
  $scheme = $this->get_s3_url_scheme();
1716
  $region = '';
1717
  }
1718
 
1719
+ $size = $this->maybe_convert_size_to_string( $post_id, $size );
 
 
 
1720
 
1721
+ // Force use of secured URL when ACL has been set to private
1722
+ if ( is_null( $expires ) ) {
1723
+ if ( is_null( $size ) && isset( $s3object['acl'] ) && self::PRIVATE_ACL === $s3object['acl'] ) {
1724
+ // Full size URL private
1725
+ $expires = self::DEFAULT_EXPIRES;
1726
+ }
1727
+
1728
+ if ( ! is_null( $size ) && isset( $s3object['sizes'][ $size ]['acl'] ) && self::PRIVATE_ACL === $s3object['sizes'][ $size ]['acl'] ) {
1729
+ // Alternative size URL private
1730
+ $expires = self::DEFAULT_EXPIRES;
1731
+ }
1732
+ }
1733
 
1734
  if ( ! is_null( $size ) ) {
1735
  if ( is_null( $meta ) ) {
1740
  return $meta;
1741
  }
1742
 
1743
+ if ( ! empty( $meta ) && isset( $meta['sizes'][ $size ]['file'] ) ) {
1744
  $size_prefix = dirname( $s3object['key'] );
1745
  $size_file_prefix = ( '.' === $size_prefix ) ? '' : $size_prefix . '/';
1746
 
1761
 
1762
  $s3object['key'] = $this->maybe_update_cloudfront_path( $s3object['key'] );
1763
 
1764
+ $domain_bucket = $this->get_s3_url_domain( $s3object['bucket'], $region, $expires );
1765
+ $file = $this->encode_filename_in_path( $s3object['key'] );
1766
+ $url = $scheme . '://' . $domain_bucket . '/' . $file;
1767
 
1768
  return apply_filters( 'as3cf_get_attachment_url', $url, $s3object, $post_id, $expires, $headers );
1769
  }
1770
 
1771
+ /**
1772
+ * Get attachment url
1773
+ *
1774
+ * @param string $url
1775
+ * @param int $post_id
1776
+ *
1777
+ * @return bool|mixed|void|WP_Error
1778
+ */
1779
+ public function wp_get_attachment_url( $url, $post_id ) {
1780
+ $new_url = $this->get_attachment_url( $post_id );
1781
+
1782
+ if ( false === $new_url ) {
1783
+ return $url;
1784
+ }
1785
+
1786
+ $new_url = apply_filters( 'wps3_get_attachment_url', $new_url, $post_id, $this ); // Old naming convention, will be deprecated soon
1787
+ $new_url = apply_filters( 'as3cf_wp_get_attachment_url', $new_url, $post_id );
1788
+
1789
+ return $new_url;
1790
+ }
1791
+
1792
  /**
1793
  * Maybe encode attachment URLs when retrieving the image tag
1794
  *
1802
  * @return string
1803
  */
1804
  public function maybe_encode_get_image_tag( $html, $id, $alt, $title, $align, $size ) {
1805
+ if ( ! ( $s3object = $this->is_attachment_served_by_s3( $id ) ) ) {
1806
+ // Not served by S3, return
1807
  return $html;
1808
  }
1809
 
1815
  }
1816
 
1817
  $img_src = $matches[1];
1818
+ $new_img_src = $this->maybe_sign_intermediate_size( $img_src, $id, $size, $s3object );
1819
+ $new_img_src = $this->encode_filename_in_path( $new_img_src );
1820
 
1821
+ return str_replace( $img_src, $new_img_src, $html );
1822
  }
1823
 
1824
  /**
1832
  * @return array
1833
  */
1834
  public function maybe_encode_wp_get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
1835
+ if ( ! ( $s3object = $this->is_attachment_served_by_s3( $attachment_id ) ) ) {
1836
+ // Not served by S3, return
1837
  return $image;
1838
  }
1839
 
1840
  if ( isset( $image[0] ) ) {
1841
+ $url = $this->maybe_sign_intermediate_size( $image[0], $attachment_id, $size, $s3object );
1842
+ $url = $this->encode_filename_in_path( $url );
1843
+
1844
+ $image[0] = $url;
1845
  }
1846
 
1847
  return $image;
1857
  * @return array
1858
  */
1859
  public function maybe_encode_wp_prepare_attachment_for_js( $response, $attachment, $meta ) {
1860
+ if ( ! ( $s3object = $this->is_attachment_served_by_s3( $attachment->ID ) ) ) {
1861
+ // Not served by S3, return
1862
  return $response;
1863
  }
1864
 
1865
  if ( isset( $response['url'] ) ) {
1866
+ $response['url'] = $this->encode_filename_in_path( $response['url'] );
1867
  }
1868
 
1869
  if ( isset( $response['sizes'] ) && is_array( $response['sizes'] ) ) {
1870
+ foreach ( $response['sizes'] as $size => $value ) {
1871
+ $url = $this->maybe_sign_intermediate_size( $value['url'], $attachment->ID, $size, $s3object );
1872
+ $url = $this->encode_filename_in_path( $url );
1873
+
1874
+ $response['sizes'][ $size ]['url'] = $url;
1875
  }
1876
  }
1877
 
1888
  * @return array
1889
  */
1890
  public function maybe_encode_image_get_intermediate_size( $data, $post_id, $size ) {
1891
+ if ( ! ( $s3object = $this->is_attachment_served_by_s3( $post_id ) ) ) {
1892
+ // Not served by S3, return
1893
  return $data;
1894
  }
1895
 
1896
  if ( isset( $data['url'] ) ) {
1897
+ $url = $this->maybe_sign_intermediate_size( $data['url'], $post_id, $size, $s3object );
1898
+ $url = $this->encode_filename_in_path( $url );
1899
+
1900
+ $data['url'] = $url;
1901
  }
1902
 
1903
  return $data;
1904
  }
1905
 
1906
+ /**
1907
+ * Sign intermediate size.
1908
+ *
1909
+ * @param string $url
1910
+ * @param int $attachment_id
1911
+ * @param string|array $size
1912
+ * @param bool|array $s3object
1913
+ *
1914
+ * @return mixed|WP_Error
1915
+ */
1916
+ protected function maybe_sign_intermediate_size( $url, $attachment_id, $size, $s3object = false ) {
1917
+ if ( ! $s3object ) {
1918
+ $s3object = $this->get_attachment_s3_info( $attachment_id );
1919
+ }
1920
+
1921
+ $size = $this->maybe_convert_size_to_string( $attachment_id, $size );
1922
+
1923
+ if ( isset( $s3object['sizes'][ $size ] ) ) {
1924
+ // Private file, add AWS signature if required
1925
+ return $this->get_attachment_s3_url( $attachment_id, $s3object, null, $size );
1926
+ }
1927
+
1928
+ return $url;
1929
+ }
1930
+
1931
+ /**
1932
+ * Convert dimensions to size
1933
+ *
1934
+ * @param int $attachment_id
1935
+ * @param array $dimensions
1936
+ *
1937
+ * @return null|string
1938
+ */
1939
+ protected function convert_dimensions_to_size_name( $attachment_id, $dimensions ) {
1940
+ $w = $dimensions[0];
1941
+ $h = $dimensions[1];
1942
+ $original_aspect_ratio = $w / $h;
1943
+ $meta = wp_get_attachment_metadata( $attachment_id );
1944
+
1945
+ if ( ! isset( $meta['sizes'] ) || empty( $meta['sizes'] ) ) {
1946
+ return null;
1947
+ }
1948
+
1949
+ $sizes = $meta['sizes'];
1950
+ uasort( $sizes, function( $a, $b ) {
1951
+ // Order by image area
1952
+ return ( $a['width'] * $a['height'] ) - ( $b['width'] * $b['height'] );
1953
+ } );
1954
+
1955
+ $nearest_matches = array();
1956
+
1957
+ foreach ( $sizes as $size => $value ) {
1958
+ if ( $w > $value['width'] || $h > $value['height'] ) {
1959
+ continue;
1960
+ }
1961
+
1962
+ $aspect_ratio = $value['width'] / $value['height'];
1963
+
1964
+ if ( $aspect_ratio === $original_aspect_ratio ) {
1965
+ return $size;
1966
+ }
1967
+
1968
+ $nearest_matches[] = $size;
1969
+ }
1970
+
1971
+ // Return nearest match
1972
+ if ( ! empty( $nearest_matches ) ) {
1973
+ return $nearest_matches[0];
1974
+ }
1975
+
1976
+ return null;
1977
+ }
1978
+
1979
+ /**
1980
+ * Maybe convert size to string
1981
+ *
1982
+ * @param int $attachment_id
1983
+ * @param mixed $size
1984
+ *
1985
+ * @return null|string
1986
+ */
1987
+ protected function maybe_convert_size_to_string( $attachment_id, $size ) {
1988
+ if ( is_array( $size ) ) {
1989
+ return $this->convert_dimensions_to_size_name( $attachment_id, $size );
1990
+ }
1991
+
1992
+ return $size;
1993
+ }
1994
+
1995
  /**
1996
  * Is attachment served by S3.
1997
  *
2018
  * Encode file names according to RFC 3986 when generating urls
2019
  * As per Amazon https://forums.aws.amazon.com/thread.jspa?threadID=55746#jive-message-244233
2020
  *
2021
+ * @param string $file
 
2022
  *
2023
+ * @return string Encoded filename
2024
  */
2025
+ public function encode_filename_in_path( $file ) {
2026
  $url = parse_url( $file );
2027
 
2028
  if ( ! isset( $url['path'] ) ) {
2030
  return $file;
2031
  }
2032
 
2033
+ $file_name = basename( $url['path'] );
2034
+
2035
+ if ( false !== strpos( $file_name, '%' ) ) {
2036
+ // File name already encoded, return original
2037
  return $file;
2038
  }
2039
 
 
 
 
2040
  $encoded_file_name = rawurlencode( $file_name );
 
2041
 
2042
  if ( $file_name === $encoded_file_name ) {
2043
  // File name doesn't need encoding, return original
2044
  return $file;
2045
  }
2046
 
 
 
 
 
 
 
2047
  return str_replace( $file_name, $encoded_file_name, $file );
2048
  }
2049
 
2050
  /**
2051
+ * Decode file name.
2052
  *
2053
+ * @param string $file
 
2054
  *
2055
+ * @return string
2056
  */
2057
+ public function decode_filename_in_path( $file ) {
2058
+ $url = parse_url( $file );
2059
 
2060
+ if ( ! isset( $url['path'] ) ) {
2061
+ // Can't determine path, return original
2062
+ return $file;
2063
+ }
2064
 
2065
+ $file_name = basename( $url['path'] );
 
 
 
 
 
 
 
2066
 
2067
+ if ( false === strpos( $file_name, '%' ) ) {
2068
+ // File name not encoded, return original
2069
+ return $file;
2070
  }
2071
 
2072
+ $decoded_file_name = rawurldecode( $file_name );
2073
+
2074
+ return str_replace( $file_name, $decoded_file_name, $file );
2075
  }
2076
 
2077
  /**
3017
  if ( $acl == self::DEFAULT_ACL ) {
3018
  unset( $s3object['acl'] );
3019
  }
3020
+
3021
  update_post_meta( $post_id, 'amazonS3_info', $s3object );
3022
  } catch ( Exception $e ) {
3023
  $msg = 'Error setting ACL to ' . $acl . ' for ' . $s3object['key'] . ': ' . $e->getMessage();
3367
  $output .= "\r\n";
3368
  $output .= "\r\n";
3369
 
3370
+ $output .= "Local URL:\r\n";
3371
+ $output .= $this->get_local_url_preview( $escape );
3372
+ $output .= "\r\n";
3373
+ $output .= "S3 URL:\r\n";
3374
  $output .= $this->get_url_preview( $escape );
3375
  $output .= "\r\n";
3376
  $output .= "\r\n";
3671
  }
3672
 
3673
  // Original file
3674
+ $paths['full'] = $original_file;
3675
 
3676
  // Sizes
3677
  if ( isset( $meta['sizes'] ) ) {
3678
+ foreach ( $meta['sizes'] as $size => $file ) {
3679
+ if ( isset( $file['file'] ) ) {
3680
+ $paths[ $size ] = str_replace( $file_name, $file['file'], $file_path );
3681
  }
3682
  }
3683
  }
3816
  * true - Attachments only uploaded to S3
3817
  * false - Attachments not uploaded to S3
3818
  *
3819
+ * @return int
3820
  */
3821
  public function count_attachments( $prefix, $uploaded_to_s3 = null ) {
3822
  global $wpdb;
3837
 
3838
  $sql .= ' ' . $where;
3839
 
3840
+ return (int) $wpdb->get_var( $sql );
3841
  }
3842
 
3843
  /**
3975
  * @param string $variable
3976
  * @param int $type
3977
  * @param int $filter
3978
+ * @param mixed $options
3979
  *
3980
  * @return mixed
3981
  */
3982
+ public function filter_input( $variable, $type = INPUT_GET, $filter = FILTER_DEFAULT, $options = array() ) {
3983
+ return filter_input( $type, $variable, $filter, $options );
3984
  }
3985
 
3986
  /**
4029
  return $domain;
4030
  }
4031
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4032
  /**
4033
  * Display a notice if using setting to force HTTP as url scheme, removed in 1.3.
4034
  */
4070
 
4071
  return $path;
4072
  }
4073
+
4074
+ /**
4075
+ * Add the S3 meta box to the attachment screen
4076
+ */
4077
+ public function attachment_s3_meta_box() {
4078
+ add_meta_box( 's3-actions', __( 'Amazon S3', 'amazon-s3-and-cloudfront' ), array( $this, 'attachment_s3_actions_meta_box' ), 'attachment', 'side', 'core' );
4079
+ }
4080
+
4081
+ /**
4082
+ * Check we can do the media actions
4083
+ *
4084
+ * @return bool
4085
+ */
4086
+ public function verify_media_actions() {
4087
+ return false;
4088
+ }
4089
+
4090
+ /**
4091
+ * Render the S3 attachment meta box
4092
+ */
4093
+ public function attachment_s3_actions_meta_box() {
4094
+ global $post;
4095
+ $file = get_attached_file( $post->ID, true );
4096
+
4097
+ $args = array(
4098
+ 's3object' => $this->get_formatted_s3_info( $post->ID ),
4099
+ 'post' => $post,
4100
+ 'local_file_exists' => file_exists( $file ),
4101
+ 'user_can_perform_actions' => $this->verify_media_actions(),
4102
+ 'sendback' => 'post.php?post=' . $post->ID . '&action=edit',
4103
+ );
4104
+
4105
+ $this->render_view( 'attachment-metabox', $args );
4106
+ }
4107
+
4108
+ /**
4109
+ * Get ACL value string.
4110
+ *
4111
+ * @param array $acl
4112
+ *
4113
+ * @return string
4114
+ */
4115
+ protected function get_acl_value_string( $acl ) {
4116
+ return $acl['name'];
4117
+ }
4118
+
4119
+ /**
4120
+ * Return a formatted S3 info with display friendly defaults
4121
+ *
4122
+ * @param int $id
4123
+ * @param array|null $s3object
4124
+ *
4125
+ * @return array
4126
+ */
4127
+ public function get_formatted_s3_info( $id, $s3object = null ) {
4128
+ if ( is_null( $s3object ) ) {
4129
+ if ( ! ( $s3object = $this->get_attachment_s3_info( $id ) ) ) {
4130
+ return false;
4131
+ }
4132
+ }
4133
+
4134
+ $s3object['url'] = $this->get_attachment_s3_url( $id, $s3object );
4135
+
4136
+ $acl = ( isset( $s3object['acl'] ) ) ? $s3object['acl'] : self::DEFAULT_ACL;
4137
+ $acl_info = array(
4138
+ 'acl' => $acl,
4139
+ 'name' => $this->get_acl_display_name( $acl ),
4140
+ 'title' => $this->get_media_action_strings( 'change_to_private' ),
4141
+ );
4142
+
4143
+ if ( self::PRIVATE_ACL === $acl ) {
4144
+ $acl_info['title'] = $this->get_media_action_strings( 'change_to_public' );
4145
+ }
4146
+
4147
+ $s3object['acl'] = $acl_info;
4148
+
4149
+ $regions = $this->get_aws_regions();
4150
+
4151
+ if ( isset( $s3object['region'] ) && '' == $s3object['region'] ) {
4152
+ $s3object['region'] = self::DEFAULT_REGION;
4153
+ }
4154
+
4155
+ if ( isset( $regions[ $s3object['region'] ] ) ) {
4156
+ $s3object['region'] = $regions[ $s3object['region'] ];
4157
+ }
4158
+
4159
+ return $s3object;
4160
+ }
4161
+
4162
+ /**
4163
+ * Get all strings or a specific string used for the media actions
4164
+ *
4165
+ * @param null|string $string
4166
+ *
4167
+ * @return array|string
4168
+ */
4169
+ public function get_media_action_strings( $string = null ) {
4170
+ $strings = apply_filters( 'as3cf_media_action_strings', array(
4171
+ 'amazon_s3' => __( 'Amazon S3', 'amazon-s3-and-cloudfront' ),
4172
+ 'bucket' => _x( 'Bucket', 'Amazon S3 bucket', 'amazon-s3-and-cloudfront' ),
4173
+ 'key' => _x( 'Path', 'Path to file on Amazon S3', 'amazon-s3-and-cloudfront' ),
4174
+ 'region' => _x( 'Region', 'Location of Amazon S3 bucket', 'amazon-s3-and-cloudfront' ),
4175
+ 'acl' => _x( 'Access', 'Access control list of the file on Amazon S3', 'amazon-s3-and-cloudfront' ),
4176
+ 'url' => __( 'URL', 'amazon-s3-and-cloudfront' ),
4177
+ ) );
4178
+
4179
+ if ( ! is_null( $string ) ) {
4180
+ return isset( $strings[ $string ] ) ? $strings[ $string ] : '';
4181
+ }
4182
+
4183
+ return $strings;
4184
+ }
4185
+
4186
+ /**
4187
+ * Load media assets.
4188
+ */
4189
+ public function load_media_assets() {
4190
+ $version = $this->get_asset_version();
4191
+ $suffix = $this->get_asset_suffix();
4192
+
4193
+ $src = plugins_url( 'assets/css/media.css', $this->plugin_file_path );
4194
+ wp_enqueue_style( 'as3cf-media-styles', $src, array( 'as3cf-modal' ), $version );
4195
+
4196
+ $src = plugins_url( 'assets/js/media' . $suffix . '.js', $this->plugin_file_path );
4197
+ wp_enqueue_script( 'as3cf-media-script', $src, array( 'jquery', 'media-views', 'media-grid', 'wp-util' ), $version, true );
4198
+
4199
+ wp_localize_script( 'as3cf-media-script',
4200
+ 'as3cf_media',
4201
+ array(
4202
+ 'strings' => $this->get_media_action_strings(),
4203
+ 'nonces' => array(
4204
+ 'get_attachment_s3_details' => wp_create_nonce( 'get-attachment-s3-details' ),
4205
+ )
4206
+ )
4207
+ );
4208
+ }
4209
+
4210
+ /**
4211
+ * Handle retieving the S3 details for attachment modals.
4212
+ */
4213
+ public function ajax_get_attachment_s3_details() {
4214
+ if ( ! isset( $_POST['id'] ) ) {
4215
+ return;
4216
+ }
4217
+
4218
+ check_ajax_referer( 'get-attachment-s3-details', '_nonce' );
4219
+
4220
+ $id = intval( $_POST['id'] );
4221
+
4222
+ // get the actions available for the attachment
4223
+ $data = array(
4224
+ 'links' => $this->add_media_row_actions( array(), $id ),
4225
+ 's3object' => $this->get_formatted_s3_info( $id ),
4226
+ 'acl_toggle' => $this->verify_media_actions(),
4227
+ );
4228
+
4229
+ wp_send_json_success( $data );
4230
+ }
4231
+
4232
+ /**
4233
+ * Conditionally adds copy, remove and download S3 action links for an
4234
+ * attachment on the Media library list view
4235
+ *
4236
+ * @param array $actions
4237
+ * @param WP_Post|int $post
4238
+ *
4239
+ * @return array
4240
+ */
4241
+ function add_media_row_actions( $actions = array(), $post ) {
4242
+ return $actions;
4243
+ }
4244
+
4245
+ /**
4246
+ * Load the attachment assets only when editing an attachment
4247
+ *
4248
+ * @param $hook_suffix
4249
+ */
4250
+ public function load_attachment_assets( $hook_suffix ) {
4251
+ $version = $this->get_asset_version();
4252
+ $suffix = $this->get_asset_suffix();
4253
+
4254
+ global $post;
4255
+ if ( 'post.php' != $hook_suffix || 'attachment' != $post->post_type ) {
4256
+ return;
4257
+ }
4258
+
4259
+ $src = plugins_url( 'assets/css/attachment.css', $this->plugin_file_path );
4260
+ wp_enqueue_style( 'as3cf-pro-attachment-styles', $src, array( 'as3cf-modal' ), $version );
4261
+
4262
+ do_action( 'as3cf_load_attachment_assets', $version, $suffix );
4263
+ }
4264
+
4265
+ /**
4266
+ * Maybe remove query string from URL.
4267
+ *
4268
+ * @param string $url
4269
+ *
4270
+ * @return string
4271
+ */
4272
+ public function maybe_remove_query_string( $url ) {
4273
+ $parts = explode( '?', $url );
4274
+
4275
+ return reset( $parts );
4276
+ }
4277
+
4278
+ /**
4279
+ * Normalize object prefix.
4280
+ *
4281
+ * @param string $prefix
4282
+ *
4283
+ * @return string
4284
+ */
4285
+ protected function normalize_object_prefix( $prefix ) {
4286
+ $directory = dirname( $prefix );
4287
+
4288
+ return ( '.' === $directory ) ? '' : $directory . '/';
4289
+ }
4290
+
4291
+ /**
4292
+ * Remove scheme from URL.
4293
+ *
4294
+ * @param string $url
4295
+ *
4296
+ * @return string
4297
+ */
4298
+ public function remove_scheme( $url ) {
4299
+ return preg_replace( '/^(?:http|https):/', '', $url );
4300
+ }
4301
+
4302
+ /**
4303
+ * Remove size from filename (image[-100x100].jpeg).
4304
+ *
4305
+ * @param string $url
4306
+ * @param bool $remove_extension
4307
+ *
4308
+ * @return string
4309
+ */
4310
+ public function remove_size_from_filename( $url, $remove_extension = false ) {
4311
+ $url = preg_replace( '/^(\S+)-[0-9]{1,4}x[0-9]{1,4}(\.[a-zA-Z0-9\.]{2,})?/', '$1$2', $url );
4312
+
4313
+ if ( $remove_extension ) {
4314
+ $parts = pathinfo( $url );
4315
+ $url = str_replace( '.' . $parts['extension'], '', $url );
4316
+ }
4317
+
4318
+ return $url;
4319
+ }
4320
+
4321
+ /**
4322
+ * Update site optio