WP Offload S3 Lite - Version 1.1

Version Description

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.

Download this release

Release Info

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

Code changes from version 1.0.5 to 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,40 @@ 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 +115,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
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 - 2016-09-29 ###
73
+ * New: Filter post content. S3 URLs will no longer be saved to the database
74
+ * New: Upgrade routine to replace all S3 URLs in content with local URLs
75
+ * New: Support for theme custom logos
76
+ * New: Control the ACL for intermediate image sizes using the `as3cf_upload_acl_sizes` filter
77
+ * Bug fix: File names containing special characters double encoded
78
+ * Bug fix: `srcset` not working for file names containing special characters
79
+ * Bug fix: Incorrect placeholder text for 'Path' option
80
+ * Bug fix: Objects in root of bucket not deleted when removed from the Media Library
81
+ * Bug fix: No longer use deprecated functions in WordPress 4.6
82
+ * Bug fix: Don't delete local file when 'Remove Files From Server' enabled and upload to S3 fails
83
+
84
### WP Offload S3 Lite 1.0.5 - 2016-09-01 ###
85
* New: Compatibility with WordPress 4.6
86
* Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/)
87
88
### WP Offload S3 Lite 1.0.4 - 2016-05-30 ###
89
+ * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
90
+ * New: `as3cf_cloudfront_path_parts` filter allows changing served CloudFront path (useful when distribution pulls subdirectory)
91
+ * Improvement: Better compatibility with non-standard notices from other plugins and themes
92
+ * Improvement: Added basic auth and proxy info to diagnostic info
93
+ * Improvement: Added `allow_url_fopen` status to diagnostic info
94
+ * Improvement: Added memory usage to diagnostic info
95
+ * Improvement: Ensure notice text is 800px or less in width
96
+ * Improvement: Reduced database queries on settings screen
97
+ * Bug fix: Properly handle _wp_attachment_data metadata when it is a serialized WP_Error
98
99
### WP Offload S3 Lite 1.0.3 - 2016-03-23 ###
100
* Bug fix: Don't replace srcset URLs when Rewrite File URLs option disabled
115
* Improvement: Far future expiration header set by default
116
* Improvement: Newly created bucket now immediately appears in the bucket list
117
* Improvement: Cleanup user meta on uninstall
118
+ * Improvement: WP Retina 2x integration removed
119
* 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
120
* Bug fix: Responsive srcset PHP notices
121
* 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/js/tinymce.min.js ADDED
@@ -0,0 +1 @@
1
+ tinymce.PluginManager.add("as3cf",function(a){function b(a){var b=[];if(h.each(a,function(a,c){h.has(j,a)||b.push(a)}),0!==b.length&&!(h.isUndefined(i.frame)||h.isUndefined(i.frame.states)||h.isUndefined(i.frame.states.models))){var c=h.find(i.frame.states.models,function(a){return"insert"===a.id});if(!h.isUndefined(c)&&0!==h.size(c.attributes.library.models)){var d=c.attributes.library.models,e={};h.each(a,function(a,b){var c=a.replace(/-[0-9]{1,4}x[0-9]{1,4}(\.[a-z]{2,4})#x2F;,"$1"),f=h.find(d,function(a){return a.attributes.url===c});if(!h.isUndefined(f)){var g=h.find(f.attributes.sizes,function(b){return a===b.local_url});return h.isUndefined(g)?!1:void(e[a]=g.url)}}),h.extend(j,e)}}}function c(a){var b=[];h.each(a,function(a,c){h.has(j,a)||b.push(a)}),0!==b.length&&g.ajax({url:ajaxurl,type:"POST",dataType:"JSON",async:!1,cache:!1,data:{action:"as3cf_tinymce_fetch_urls",_nonce:as3cf_tinymce.fetch_urls_nonce,urls:b},success:function(a){j=h.extend(j,a.data)}})}function d(a){return h.has(j,a)?j[a]:!1}function e(a){var e=new RegExp("("+as3cf_tinymce.local_url_escaped+'[^"<\\s]*)',"g"),f=a.match(e);return null===f?a:(b(f),c(f),h.each(f,function(b,c){var e=d(b);!1!==e&&(a=a.replace(new RegExp(b,"g"),e))}),a)}function f(a){return h.each(j,function(b,c){!1!==b&&(a=a.replace(new RegExp(b,"g"),c))}),a}var g=window.jQuery,h=window._,i=window.wp.media,j={};a.on("BeforeSetContent",function(a){a.content=e(a.content)}),a.on("PostProcess",function(a){a.get&&(a.content=f(a.content))})});
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( $file );
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 option.
4323
+ *
4324
+ * @param string $option
4325
+ * @param mixed $value
4326
+ * @param bool $autoload
4327
+ *
4328
+ * @return bool
4329
+ */
4330
+ public function update_site_option( $option, $value, $autoload = true ) {
4331
+ if ( is_multisite() ) {
4332
+ return update_site_option( $option, $value );
4333
+ }
4334
+
4335
+ return update_option( $option, $value, $autoload );
4336
+ }
4337
}
classes/as3cf-filter.php ADDED
@@ -0,0 +1,687 @@
1
+ <?php
2
+
3
+ abstract class AS3CF_Filter {
4
+
5
+ /**
6
+ * @var Amazon_S3_And_CloudFront
7
+ */
8
+ protected $as3cf;
9
+
10
+ /**
11
+ * @var array
12
+ */
13
+ protected $query_cache = array();
14
+
15
+ /**
16
+ * Constructor
17
+ *
18
+ * @param Amazon_S3_And_CloudFront $as3cf
19
+ */
20
+ public function __construct( $as3cf ) {
21
+ $this->as3cf = $as3cf;
22
+
23
+ // Purge on attachment delete
24
+ add_action( 'delete_attachment', array( $this, 'purge_cache_on_attachment_delete' ) );
25
+
26
+ $this->init();
27
+ }
28
+
29
+ /**
30
+ * Filter EDD download files.
31
+ *
32
+ * @param array $value
33
+ *
34
+ * @return array
35
+ */
36
+ public function filter_edd_download_files( $value ) {
37
+ if ( ! $this->should_filter_content() ) {
38
+ // Not filtering content, return
39
+ return $value;
40
+ }
41
+
42
+ if ( empty( $value ) ) {
43
+ // Nothing to filter, return
44
+ return $value;
45
+ }
46
+
47
+ foreach ( $value as $key => $attachment ) {
48
+ $url = $this->get_url( $attachment['attachment_id'] );
49
+
50
+ if ( $url ) {
51
+ $value[ $key ]['file'] = $this->get_url( $attachment['attachment_id'] );
52
+ }
53
+ }
54
+
55
+ return $value;
56
+ }
57
+
58
+ /**
59
+ * Filter customizer image.
60
+ *
61
+ * @param string $value
62
+ * @param bool|string $old_value
63
+ *
64
+ * @return string
65
+ */
66
+ public function filter_customizer_image( $value, $old_value = false ) {
67
+ if ( empty( $value ) || is_a( $value, 'stdClass' ) ) {
68
+ return $value;
69
+ }
70
+
71
+ $cache = $this->get_option_cache();
72
+ $to_cache = array();
73
+ $value = $this->process_content( $value, $cache, $to_cache );
74
+
75
+ $this->maybe_update_option_cache( $to_cache );
76
+
77
+ return $value;
78
+ }
79
+
80
+ /**
81
+ * Filter header image data.
82
+ *
83
+ * @param stdClass $value
84
+ * @param bool|stdClass $old_value
85
+ *
86
+ * @return stdClass
87
+ */
88
+ public function filter_header_image_data( $value, $old_value = false ) {
89
+ $url = $this->get_url( $value->attachment_id );
90
+
91
+ if ( $url ) {
92
+ $value->url = $url;
93
+ $value->thumbnail_url = $url;
94
+ }
95
+
96
+ return $value;
97
+ }
98
+
99
+ /**
100
+ * Filter post.
101
+ *
102
+ * @param string $content
103
+ *
104
+ * @return string
105
+ */
106
+ public function filter_post( $content ) {
107
+ $cache = $this->get_post_cache();
108
+ $to_cache = array();
109
+ $content = $this->process_content( $content, $cache, $to_cache );
110
+
111
+ $this->maybe_update_post_cache( $to_cache );
112
+
113
+ return $content;
114
+ }
115
+
116
+ /**
117
+ * Process content.
118
+ *
119
+ * @param string $content
120
+ * @param array $cache
121
+ * @param array $to_cache
122
+ *
123
+ * @return mixed
124
+ */
125
+ protected function process_content( $content, $cache, &$to_cache ) {
126
+ if ( ! $this->should_filter_content() ) {
127
+ // Not filtering content, return
128
+ return $content;
129
+ }
130
+
131
+ $content = $this->pre_replace_content( $content );
132
+
133
+ // Find URLs from img src
134
+ $url_pairs = $this->get_urls_from_img_src( $content, $to_cache );
135
+ $content = $this->replace_urls( $content, $url_pairs );
136
+
137
+ // Find leftover URLs
138
+ $content = $this->find_urls_and_replace( $content, $cache, $to_cache );
139
+
140
+ // Perform post processing if required
141
+ $content = $this->post_process_content( $content );
142
+
143
+ return $content;
144
+ }
145
+
146
+ /**
147
+ * Find URLs and replace.
148
+ *
149
+ * @param string $value
150
+ * @param array $cache
151
+ * @param array $to_cache
152
+ *
153
+ * @return string
154
+ */
155
+ protected function find_urls_and_replace( $value, $cache, &$to_cache ) {
156
+ if ( ! $this->should_filter_content() ) {
157
+ // Not filtering content, return
158
+ return $value;
159
+ }
160
+
161
+ $url_pairs = $this->get_urls_from_content( $value, $cache, $to_cache );
162
+ $value = $this->replace_urls( $value, $url_pairs );
163
+
164
+ return $value;
165
+ }
166
+
167
+ /**
168
+ * Get URLs from img src.
169
+ *
170
+ * @param string $content
171
+ * @param array $to_cache
172
+ *
173
+ * @return array
174
+ */
175
+ protected function get_urls_from_img_src( $content, &$to_cache ) {
176
+ $url_pairs = array();
177
+
178
+ if ( ! preg_match_all( '/<img [^>]+>/', $content, $matches ) || ! isset( $matches[0] ) ) {
179
+ // No img tags found, return
180
+ return $url_pairs;
181
+ }
182
+
183
+ $matches = array_unique( $matches[0] );
184
+ $attachment_ids = array();
185
+
186
+ foreach ($matches as $image) {
187
+ if ( ! preg_match( '/src=\\\?["\']+([^"\'\\\]+)/', $image, $src ) || ! isset( $src[1] ) ) {
188
+ // Can't determine URL, skip
189
+ continue;
190
+ }
191
+
192
+ $url = $src[1];
193
+
194
+ if ( ! $this->url_needs_replacing( $url ) ) {
195
+ // URL already correct, skip
196
+ continue;
197
+ }
198
+
199
+ if ( ! preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) || ! isset( $class_id[1] ) ) {
200
+ // Can't determine ID from class, skip
201
+ continue;
202
+ }
203
+
204
+ $attachment_ids[ absint( $class_id[1] ) ] = $url;
205
+ }
206
+
207
+ if ( count( $attachment_ids ) > 1 ) {
208
+ /*
209
+ * Warm object cache for use with 'get_post_meta()'.
210
+ *
211
+ * To avoid making a database call for each image, a single query
212
+ * warms the object cache with the meta information for all images.
213
+ */
214
+ update_meta_cache( 'post', array_keys( $attachment_ids ) );
215
+ }
216
+
217
+ foreach ( $attachment_ids as $attachment_id => $url ) {
218
+ if ( ! $this->attachment_id_matches_src( $attachment_id, $url ) ) {
219
+ // Path doesn't match attachment, skip
220
+ continue;
221
+ }
222
+
223
+ $this->push_to_url_pairs( $url_pairs, $attachment_id, $url, $to_cache );
224
+ }
225
+
226
+ return $url_pairs;
227
+ }
228
+
229
+ /**
230
+ * Get URLs from content.
231
+ *
232
+ * @param string $content
233
+ * @param array $cache
234
+ * @param array $to_cache
235
+ *
236
+ * @return array
237
+ */
238
+ protected function get_urls_from_content( $content, $cache, &$to_cache ) {
239
+ $url_pairs = array();
240
+
241
+ if ( ! preg_match_all( '/(http|https)?:?\/\/[^"\'\s<>\\\]*/', $content, $matches ) || ! isset( $matches[0] ) ) {
242
+ // No URLs found, return
243
+ return $url_pairs;
244
+ }
245
+
246
+ $matches = array_unique( $matches[0] );
247
+
248
+ foreach ($matches as $url) {
249
+ if ( ! $this->url_needs_replacing( $url ) ) {
250
+ // URL already correct, skip
251
+ continue;
252
+ }
253
+
254
+ $parts = parse_url( $url );
255
+
256
+ if ( ! isset( $parts['path'] ) ) {
257
+ // URL doesn't have a path, continue
258
+ continue;
259
+ }
260
+
261
+ $info = pathinfo( $parts['path'] );
262