WP Offload S3 Lite - Version 0.9.4

Version Description

  • 2015-08-27 =
  • New: Update all existing attachments with missing file sizes when the 'Remove Files From Server' option is enabled (automatically runs in the background)
  • Improvement: Show when constants are used to set bucket and region options
  • Improvement: Don't show compatibility notices on plugin update screen
  • Improvement: On Multisite installs don't call restore_current_blog() on successive loop iterations
  • Bug fix: 'Error getting URL preview' alert shown when enter key pressed on settings screen
  • Bug fix: Unable to crop header images when the 'Remove Files From Server' option is enabled
  • Bug fix: Incorrect storage space shown on Multisite installs when the 'Remove Files From Server' option is enabled
  • Bug fix: Upload attempted to non existent bucket when defined by constant
  • Bug fix: 'SignatureDoesNotMatch' error shown when using signed URLs with bucket names containing '.' characters
Download this release

Release Info

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

Code changes from version 0.9.3 to 0.9.4

README.md CHANGED
@@ -1,10 +1,9 @@
1
  # WP Offload S3 #
2
- **Contributors:** bradt
3
- **Donate link:** https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5VPMGLLK94XJC
4
  **Tags:** uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  **Requires at least:** 3.7
6
  **Tested up to:** 4.3
7
- **Stable tag:** 0.9.3
8
  **License:** GPLv3
9
 
10
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
@@ -17,19 +16,21 @@ This plugin automatically copies images, videos, documents, and any other media
17
 
18
  Uploading files *directly* to your S3 account is not currently supported by this plugin. They are uploaded to your server first, then copied to S3. There is an option to automatically remove the files from your server once they are copied to S3 however.
19
 
20
- If you're adding this plugin to a site that's been around for a while, your existing media files will not be copied or served from S3. Only newly uploaded files will be copied and served from S3.
21
 
22
  **PRO Upgrade with Email Support and More Features**
23
 
24
  * Upload existing Media Library to S3
25
  * Find & replace file URLs in content
26
  * Control S3 files from the Media Library
27
- * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#assets-addon) - Serve your CSS & JS from S3/CloudFront
28
- * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#woocommerce-addon)
29
- * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#edd-addon)
30
  * PriorityExpert™ email support
31
 
32
- See the video below or [visit the web site](http://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin) to learn more about the pro version.
 
 
33
 
34
  https://www.youtube.com/watch?v=55xNGnbJ_CY
35
 
@@ -66,6 +67,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
66
 
67
  ## Changelog ##
68
 
 
 
 
 
 
 
 
 
 
 
 
69
  ### 0.9.3 - 2015-08-17 ###
70
  * New: Pro upgrade sidebar
71
  * Bug fix: Create buckets in US standard region causing S3 URLs to 404 errors
1
  # WP Offload S3 #
2
+ **Contributors:** bradt, deliciousbrains
 
3
  **Tags:** uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
4
  **Requires at least:** 3.7
5
  **Tested up to:** 4.3
6
+ **Stable tag:** 0.9.4
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.
16
 
17
  Uploading files *directly* to your S3 account is not currently supported by this plugin. They are uploaded to your server first, then copied to S3. There is an option to automatically remove the files from your server once they are copied to S3 however.
18
 
19
+ If you're adding this plugin to a site that's been around for a while, your existing media files will not be copied or served from S3. Only newly uploaded files will be copied and served from S3. The pro upgrade has an upload tool to handle existing media files.
20
 
21
  **PRO Upgrade with Email Support and More Features**
22
 
23
  * Upload existing Media Library to S3
24
  * Find & replace file URLs in content
25
  * Control 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 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)
28
+ * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#edd-addon)
29
  * PriorityExpert™ email support
30
 
31
+ [Compare pro vs free →](http://deliciousbrains.com/wp-offload-s3/upgrade/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin)
32
+
33
+ The video below runs through the pro upgrade features...
34
 
35
  https://www.youtube.com/watch?v=55xNGnbJ_CY
36
 
67
 
68
  ## Changelog ##
69
 
70
+ ### 0.9.4 - 2015-08-27 ###
71
+ * New: Update all existing attachments with missing file sizes when the 'Remove Files From Server' option is enabled (automatically runs in the background)
72
+ * Improvement: Show when constants are used to set bucket and region options
73
+ * Improvement: Don't show compatibility notices on plugin update screen
74
+ * Improvement: On Multisite installs don't call `restore_current_blog()` on successive loop iterations
75
+ * Bug fix: 'Error getting URL preview' alert shown when enter key pressed on settings screen
76
+ * Bug fix: Unable to crop header images when the 'Remove Files From Server' option is enabled
77
+ * Bug fix: Incorrect storage space shown on Multisite installs when the 'Remove Files From Server' option is enabled
78
+ * Bug fix: Upload attempted to non existent bucket when defined by constant
79
+ * Bug fix: 'SignatureDoesNotMatch' error shown when using signed URLs with bucket names containing '.' characters
80
+
81
  ### 0.9.3 - 2015-08-17 ###
82
  * New: Pro upgrade sidebar
83
  * Bug fix: Create buckets in US standard region causing S3 URLs to 404 errors
assets/js/modal.js CHANGED
@@ -1,7 +1,8 @@
1
  var as3cfModal = (function ( $ ) {
2
 
3
  var modal = {
4
- prefix: 'as3cf'
 
5
  };
6
 
7
  var modals = {};
@@ -9,9 +10,9 @@ var as3cfModal = (function ( $ ) {
9
  /**
10
  * Target to key
11
  *
12
- * @param string target
13
  *
14
- * @return string
15
  */
16
  function targetToKey( target ) {
17
  return target.replace( /[^a-z]/g, '' );
@@ -20,10 +21,11 @@ var as3cfModal = (function ( $ ) {
20
  /**
21
  * Open modal
22
  *
23
- * @param string target
24
- * @param function callback
 
25
  */
26
- modal.open = function ( target, callback ) {
27
  var key = targetToKey( target );
28
 
29
  // Overlay
@@ -41,6 +43,10 @@ var as3cfModal = (function ( $ ) {
41
  }
42
  $modal.data( 'as3cf-modal-target', target ).append( modals[ key ] );
43
 
 
 
 
 
44
  if ( 'function' === typeof callback ) {
45
  callback( target );
46
  }
@@ -58,9 +64,13 @@ var as3cfModal = (function ( $ ) {
58
  /**
59
  * Close modal
60
  *
61
- * @param function callback
62
  */
63
  modal.close = function ( callback ) {
 
 
 
 
64
  var target = $( '#as3cf-modal' ).data( 'as3cf-modal-target' );
65
 
66
  $( '#as3cf-overlay' ).fadeOut( 150, function () {
@@ -76,6 +86,15 @@ var as3cfModal = (function ( $ ) {
76
  $( 'body' ).trigger( 'as3cf-modal-close', [ target ] );
77
  };
78
 
 
 
 
 
 
 
 
 
 
79
  // Setup click handlers
80
  $( document ).ready( function () {
81
 
@@ -99,101 +118,4 @@ var as3cfModal = (function ( $ ) {
99
 
100
  return modal;
101
 
102
- })( jQuery );
103
-
104
- var as3cfFindAndReplaceModal = (function ( $, as3cfModal ) {
105
-
106
- var modal = {
107
- selector: '.as3cf-find-replace-container',
108
- isBulk: false,
109
- link: null,
110
- payload: {}
111
- };
112
-
113
- /**
114
- * Open modal
115
- *
116
- * @param string link
117
- * @param mixed payload
118
- */
119
- modal.open = function ( link, payload ) {
120
- if ( typeof link !== 'undefined' ) {
121
- modal.link = link;
122
- }
123
- if ( typeof payload !== 'undefined' ) {
124
- modal.payload = payload;
125
- }
126
-
127
- as3cfModal.open( modal.selector );
128
-
129
- $( modal.selector ).find( '.single-file' ).show();
130
- $( modal.selector ).find( '.multiple-files' ).hide();
131
- if ( modal.isBulk ) {
132
- $( modal.selector ).find( '.single-file' ).hide();
133
- $( modal.selector ).find( '.multiple-files' ).show();
134
- }
135
- };
136
-
137
- /**
138
- * Close modal
139
- */
140
- modal.close = function () {
141
- as3cfModal.close( modal.selector );
142
- };
143
-
144
- /**
145
- * Set the isBulk flag
146
- */
147
- modal.setBulk = function ( isBulk ) {
148
- modal.isBulk = isBulk;
149
- };
150
-
151
- /**
152
- * Create the loading state
153
- */
154
- modal.startLoading = function () {
155
- $( modal.selector + ' [data-find-replace]' ).prop( 'disabled', true ).siblings( '.spinner' ).css( 'visibility', 'visible' ).show();
156
- };
157
-
158
- /**
159
- * Remove the loading state
160
- */
161
- modal.stopLoading = function () {
162
- $( modal.selector + ' [data-find-replace]' ).prop( 'disabled', false ).siblings( '.spinner' ).css( 'visibility', 'hidden' ).hide();
163
- };
164
-
165
- // Setup click handlers
166
- $( document ).ready( function () {
167
-
168
- $( 'body' ).on( 'click', modal.selector + ' [data-find-replace]', function ( e ) {
169
- var findAndReplace = $( this ).data( 'find-replace' );
170
-
171
- if ( !modal.link ) {
172
- // If there is no link set then this must be an AJAX
173
- // request so trigger an event instead
174
- $( modal.selector ).trigger( 'as3cf-find-and-replace', [ findAndReplace, modal.payload ] );
175
- return;
176
- }
177
-
178
- if ( findAndReplace ) {
179
- modal.link += '&find_and_replace=1';
180
- }
181
-
182
- modal.startLoading();
183
-
184
- window.location = modal.link;
185
- } );
186
-
187
- $( 'body' ).on( 'as3cf-modal-close', function ( e ) {
188
- modal.isBulk = false;
189
- modal.link = null;
190
- modal.payload = {};
191
- } );
192
-
193
- } );
194
-
195
- return modal;
196
-
197
- })( jQuery, as3cfModal );
198
-
199
-
1
  var as3cfModal = (function ( $ ) {
2
 
3
  var modal = {
4
+ prefix: 'as3cf',
5
+ loading: false
6
  };
7
 
8
  var modals = {};
10
  /**
11
  * Target to key
12
  *
13
+ * @param {string} target
14
  *
15
+ * @return {string}
16
  */
17
  function targetToKey( target ) {
18
  return target.replace( /[^a-z]/g, '' );
21
  /**
22
  * Open modal
23
  *
24
+ * @param {string} target
25
+ * @param {function} callback
26
+ * @param {string} customClass
27
  */
28
+ modal.open = function ( target, callback, customClass ) {
29
  var key = targetToKey( target );
30
 
31
  // Overlay
43
  }
44
  $modal.data( 'as3cf-modal-target', target ).append( modals[ key ] );
45
 
46
+ if ( undefined !== customClass ) {
47
+ $modal.addClass( customClass );
48
+ }
49
+
50
  if ( 'function' === typeof callback ) {
51
  callback( target );
52
  }
64
  /**
65
  * Close modal
66
  *
67
+ * @param {function} callback
68
  */
69
  modal.close = function ( callback ) {
70
+ if ( modal.loading ) {
71
+ return;
72
+ }
73
+
74
  var target = $( '#as3cf-modal' ).data( 'as3cf-modal-target' );
75
 
76
  $( '#as3cf-overlay' ).fadeOut( 150, function () {
86
  $( 'body' ).trigger( 'as3cf-modal-close', [ target ] );
87
  };
88
 
89
+ /**
90
+ * Set loading state
91
+ *
92
+ * @param {bool} state
93
+ */
94
+ modal.setLoadingState = function ( state ) {
95
+ modal.loading = state;
96
+ };
97
+
98
  // Setup click handlers
99
  $( document ).ready( function () {
100
 
118
 
119
  return modal;
120
 
121
+ })( 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"},d={};return c.open=function(c,e){var f=b(c);a("body").append('<div id="as3cf-overlay"></div>');var g=a("#as3cf-overlay");g.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>');var h=a("#as3cf-modal");if(void 0===d[f]){var i=a(c);d[f]=i.clone(!0).css("display","block"),i.remove()}h.data("as3cf-modal-target",c).append(d[f]),"function"==typeof e&&e(c),a("body").addClass("as3cf-modal-open"),g.fadeIn(150),h.fadeIn(150),a("body").trigger("as3cf-modal-open",[c])},c.close=function(b){var c=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){"function"==typeof b&&b(c),a("body").removeClass("as3cf-modal-open"),a(this).remove()}),a("body").trigger("as3cf-modal-close",[c])},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),as3cfFindAndReplaceModal=function(a,b){var c={selector:".as3cf-find-replace-container",isBulk:!1,link:null,payload:{}};return c.open=function(d,e){"undefined"!=typeof d&&(c.link=d),"undefined"!=typeof e&&(c.payload=e),b.open(c.selector),a(c.selector).find(".single-file").show(),a(c.selector).find(".multiple-files").hide(),c.isBulk&&(a(c.selector).find(".single-file").hide(),a(c.selector).find(".multiple-files").show())},c.close=function(){b.close(c.selector)},c.setBulk=function(a){c.isBulk=a},c.startLoading=function(){a(c.selector+" [data-find-replace]").prop("disabled",!0).siblings(".spinner").css("visibility","visible").show()},c.stopLoading=function(){a(c.selector+" [data-find-replace]").prop("disabled",!1).siblings(".spinner").css("visibility","hidden").hide()},a(document).ready(function(){a("body").on("click",c.selector+" [data-find-replace]",function(){var b=a(this).data("find-replace");return c.link?(b&&(c.link+="&find_and_replace=1"),c.startLoading(),void(window.location=c.link)):void a(c.selector).trigger("as3cf-find-and-replace",[b,c.payload])}),a("body").on("as3cf-modal-close",function(){c.isBulk=!1,c.link=null,c.payload={}})}),c}(jQuery,as3cfModal);
1
+ var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf",loading:!1},d={};return 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.js CHANGED
@@ -358,6 +358,9 @@
358
  savedSettings[ id ] = serializedForm( id );
359
  }
360
 
 
 
 
361
  $activeBucket.text( bucket );
362
  $manualBucketForm.find( '.as3cf-bucket-name' ).val( bucket );
363
  $( '#' + as3cfModal.prefix + '-bucket' ).val( bucket );
@@ -627,6 +630,16 @@
627
  generateUrlPreview();
628
  } );
629
 
 
 
 
 
 
 
 
 
 
 
630
  // Bucket select
631
  // --------------------
632
 
358
  savedSettings[ id ] = serializedForm( id );
359
  }
360
 
361
+ // Remove previous permission errors
362
+ $( '.as3cf-error.fatal' ).hide();
363
+
364
  $activeBucket.text( bucket );
365
  $manualBucketForm.find( '.as3cf-bucket-name' ).val( bucket );
366
  $( '#' + as3cfModal.prefix + '-bucket' ).val( bucket );
630
  generateUrlPreview();
631
  } );
632
 
633
+ // Don't allow 'enter' key to submit form on text input settings
634
+ $( '.as3cf-setting input[type="text"]' ).keypress( function( event ) {
635
+ if ( 13 === event.which ) {
636
+ event.preventDefault();
637
+
638
+ return false;
639
+ }
640
+
641
+ } );
642
+
643
  // Bucket select
644
  // --------------------
645
 
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(b){var c=a("#"+b),d=c.find("input[type=checkbox]");c.toggleClass("on").find("span").toggleClass("checked");var e=c.find("span.on").hasClass("checked");d.attr("checked",e).trigger("change")}function e(){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){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function f(){as3cf.buckets.bucketSelectLock=!1}var g,h={},i=/[^a-z0-9.-]/,j=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){j.hide(),g=a("#tab-"+c),g.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),g.attr("data-prefix")&&(b.prefix=g.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show")}},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){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===g.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()),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(),h=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),g.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var i={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){e.text(h),j.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c){e.text(h),e.prop("disabled",!1),"undefined"!=typeof c.success?(j.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")):j.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 g.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"),h={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(b,c,f){d.removeClass("saving"),i.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){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(i.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(i.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(i,j,k){var l=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),m=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&""===m.text()){d("copy-to-s3-wrap"),d("serve-from-s3-wrap");var n=g.attr("id");h[n]=c(n)}m.text(i),l.find(".as3cf-bucket-name").val(i),a("#"+b.prefix+"-bucket").val(i),a("#"+b.prefix+"-region").val(j),a(".updated").not(".as3cf-notice").show(),g.addClass("as3cf-has-bucket"),g.find(".as3cf-can-write-error").toggle(!k),g.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&e(),b.close(f)},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){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)):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return a.length<3||a.length>63?!1:!0===i.test(a)?!1:!0},updateNameNotice:function(b){var c=null;!0===i.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),a(".as3cf-invalid-bucket-name").html(c&&b.length>0?c:"")}},a(document).ready(function(){var f=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(f),window.location.hash){var i=window.location.hash.substring(1);as3cf.tabs.toggle(i,!0)}else g=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}}),j.length&&j.each(function(a,b){h[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(h)){var b=g.attr("id");return c(b)!==h[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(){a(this).hasClass("disabled")||d(a(this).attr("id"))}),j.on("change",".sub-toggle",function(){var b=a(this).attr("id");a(".as3cf-setting."+b).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(){var b=a(this).closest('input:radio[name="domain"]:checked'),c=b.val(),d=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),e="cloudfront"===c;d.toggleClass("hide",!e)}),a(".as3cf-ssl").on("change",'input[type="radio"]',function(){var b=a('input:radio[name="ssl"]:checked').val();if("https"===b){var c=a('input:radio[name="domain"]:checked').val();"subdomain"===c&&a('input[name="domain"][value="path"]').attr("checked",!0),a(".subdomain-wrap input").attr("disabled",!0),a(".subdomain-wrap").addClass("disabled")}else a(".subdomain-wrap input").removeAttr("disabled"),a(".subdomain-wrap").removeClass("disabled")}),a(".url-preview").on("change","input",function(){e()}),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()}),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(){var c=a(this).val(),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(c)?d.find("button[type=submit]").removeAttr("disabled"):d.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(c)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?c.find("button[type=submit]").attr("disabled",!0):c.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(b){var c=a("#"+b),d=c.find("input[type=checkbox]");c.toggleClass("on").find("span").toggleClass("checked");var e=c.find("span.on").hasClass("checked");d.attr("checked",e).trigger("change")}function e(){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){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function f(){as3cf.buckets.bucketSelectLock=!1}var g,h={},i=/[^a-z0-9.-]/,j=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){j.hide(),g=a("#tab-"+c),g.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),g.attr("data-prefix")&&(b.prefix=g.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show")}},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){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===g.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()),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(),h=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),g.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var i={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){e.text(h),j.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c){e.text(h),e.prop("disabled",!1),"undefined"!=typeof c.success?(j.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")):j.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 g.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"),h={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(b,c,f){d.removeClass("saving"),i.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){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(i.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(i.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(i,j,k){var l=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),m=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&""===m.text()){d("copy-to-s3-wrap"),d("serve-from-s3-wrap");var n=g.attr("id");h[n]=c(n)}a(".as3cf-error.fatal").hide(),m.text(i),l.find(".as3cf-bucket-name").val(i),a("#"+b.prefix+"-bucket").val(i),a("#"+b.prefix+"-region").val(j),a(".updated").not(".as3cf-notice").show(),g.addClass("as3cf-has-bucket"),g.find(".as3cf-can-write-error").toggle(!k),g.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&e(),b.close(f)},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){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)):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return a.length<3||a.length>63?!1:!0===i.test(a)?!1:!0},updateNameNotice:function(b){var c=null;!0===i.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),a(".as3cf-invalid-bucket-name").html(c&&b.length>0?c:"")}},a(document).ready(function(){var f=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(f),window.location.hash){var i=window.location.hash.substring(1);as3cf.tabs.toggle(i,!0)}else g=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}}),j.length&&j.each(function(a,b){h[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(h)){var b=g.attr("id");return c(b)!==h[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(){a(this).hasClass("disabled")||d(a(this).attr("id"))}),j.on("change",".sub-toggle",function(){var b=a(this).attr("id");a(".as3cf-setting."+b).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(){var b=a(this).closest('input:radio[name="domain"]:checked'),c=b.val(),d=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),e="cloudfront"===c;d.toggleClass("hide",!e)}),a(".as3cf-ssl").on("change",'input[type="radio"]',function(){var b=a('input:radio[name="ssl"]:checked').val();if("https"===b){var c=a('input:radio[name="domain"]:checked').val();"subdomain"===c&&a('input[name="domain"][value="path"]').attr("checked",!0),a(".subdomain-wrap input").attr("disabled",!0),a(".subdomain-wrap").addClass("disabled")}else a(".subdomain-wrap input").removeAttr("disabled"),a(".subdomain-wrap").removeClass("disabled")}),a(".url-preview").on("change","input",function(){e()}),a('.as3cf-setting input[type="text"]').keypress(function(a){return 13===a.which?(a.preventDefault(),!1):void 0}),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()}),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(){var c=a(this).val(),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(c)?d.find("button[type=submit]").removeAttr("disabled"):d.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(c)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -43,7 +43,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
43
  protected $default_tab = '';
44
 
45
  /**
46
- * @var array
47
  */
48
  public $hook_suffix;
49
 
@@ -84,8 +84,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
84
  $this->plugin_title = __( 'Offload S3', 'as3cf' );
85
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
86
 
87
- // fire up the plugin upgrade checker
88
- new AS3CF_Upgrade( $this );
89
 
90
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
91
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
@@ -107,6 +107,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
107
  add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
108
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
109
  add_filter( 'plugin_action_links', array( $this, 'plugin_actions_settings_link' ), 10, 2 );
 
110
 
111
  // include compatibility code for other plugins
112
  new AS3CF_Plugin_Compatibility( $this );
@@ -488,7 +489,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
488
  }
489
 
490
  // upload attachment to S3
491
- $this->upload_attachment_to_s3( $post_id, $data );
492
 
493
  return $data;
494
  }
@@ -503,10 +504,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
503
  * to cope with possible different regions
504
  * @param bool $remove_local_files
505
  *
506
- * @return array|WP_Error $s3object
507
  */
508
  function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $force_new_s3_client = false, $remove_local_files = true ) {
 
509
  if ( is_null( $data ) ) {
 
510
  $data = wp_get_attachment_metadata( $post_id, true );
511
  }
512
 
@@ -616,6 +619,26 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
616
  $file_paths = $this->get_attachment_file_paths( $post_id, true, $data );
617
  $additional_images = array();
618
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
619
  foreach ( $file_paths as $file_path ) {
620
  if ( ! in_array( $file_path, $files_to_remove ) ) {
621
  $additional_images[] = array(
@@ -624,6 +647,14 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
624
  );
625
 
626
  $files_to_remove[] = $file_path;
 
 
 
 
 
 
 
 
627
  }
628
  }
629
 
@@ -639,20 +670,43 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
639
  }
640
 
641
  if ( $remove_local_files ) {
642
- if ( $this->get_setting( 'remove-local-file' ) ) {
643
- if ( isset( $_POST['action'] ) && 'image-editor' == sanitize_key( $_POST['action'] ) && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
644
- // remove original main image after edit
645
- $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
646
- $original_file = trailingslashit( dirname( $file_path ) ) . basename( $meta['file'] );
647
- if ( file_exists( $original_file ) && ! in_array( $original_file, $files_to_remove ) ) {
648
- $files_to_remove[] = $original_file;
649
- }
650
- }
651
-
652
  $this->remove_local_files( $files_to_remove );
653
  }
654
  }
655
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  return $s3object;
657
  }
658
 
@@ -676,6 +730,13 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
676
  return $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $hidpi_suffix . '.' . $pathinfo['extension'];
677
  }
678
 
 
 
 
 
 
 
 
679
  function get_object_version_string( $post_id ) {
680
  if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
681
  $date_format = 'dHis';
@@ -708,8 +769,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
708
  return null;
709
  }
710
 
711
- // Media files attached to a post use the post's date
712
- // to determine the folder path they are placed in
 
 
 
 
 
 
 
713
  function get_attachment_folder_time( $post_id ) {
714
  $time = current_time( 'timestamp' );
715
 
@@ -815,18 +883,39 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
815
  return get_post_meta( $post_id, 'amazonS3_info', true );
816
  }
817
 
 
 
 
 
 
818
  function is_plugin_setup() {
819
- return (bool) $this->get_setting( 'bucket' ) && ! is_wp_error( $this->aws->get_client() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
820
  }
821
 
822
  /**
823
  * Generate a link to download a file from Amazon S3 using query string
824
  * authentication. This link is only valid for a limited amount of time.
825
  *
826
- * @param int $post_id Post ID of the attachment
827
- * @param int $expires Seconds for the link to live
828
- * @param string $size Size of the image to get
829
- * @param array $headers Header overrides for request
830
  *
831
  * @return mixed|void|WP_Error
832
  */
@@ -981,11 +1070,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
981
  /**
982
  * Get the url of the file from Amazon S3
983
  *
984
- * @param int $post_id Post ID of the attachment
985
- * @param int $expires Seconds for the link to live
986
- * @param string $size Size of the image to get
987
- * @param array $meta Pre retrieved _wp_attachment_metadata for the attachment
988
- * @param array $headers Header overrides for request
989
  *
990
  * @return bool|mixed|void|WP_Error
991
  */
@@ -1016,7 +1105,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1016
 
1017
  $domain_bucket = $this->get_s3_url_domain( $s3object['bucket'], $region, $expires );
1018
 
1019
- if ( $size ) {
1020
  if ( is_null( $meta ) ) {
1021
  $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
1022
  }
@@ -1027,23 +1116,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1027
 
1028
  if ( ! is_null( $expires ) ) {
1029
  try {
1030
- $expires = time() + $expires;
1031
  $secure_url = $this->get_s3client( $region )->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires, $headers );
 
 
1032
  }
1033
  catch ( Exception $e ) {
1034
  return new WP_Error( 'exception', $e->getMessage() );
1035
  }
1036
  }
1037
 
1038
- // encode file
1039
  $file = $this->encode_filename_in_path( $s3object['key'] );
 
1040
 
1041
- $url = $scheme . '://' . $domain_bucket . '/' . $file;
1042
- if ( isset( $secure_url ) ) {
1043
- $url .= substr( $secure_url, strpos( $secure_url, '?' ) );
1044
- }
1045
-
1046
- return apply_filters( 'as3cf_get_attachment_url', $url, $s3object, $post_id, $expires );
1047
  }
1048
 
1049
  /**
@@ -1410,8 +1496,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1410
  * @param Amazon_Web_Services $aws
1411
  */
1412
  function admin_menu( $aws ) {
1413
- $this->hook_suffix = $aws->add_page( $this->get_plugin_page_title(), $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
1414
- add_action( 'load-' . $this->hook_suffix , array( $this, 'plugin_load' ) );
 
 
 
 
1415
  }
1416
 
1417
  /**
@@ -1450,12 +1540,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1450
  function get_bucket_region( $bucket ) {
1451
  try {
1452
  $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
1453
- }
1454
- catch ( Exception $e ) {
1455
- $error_msg = sprintf( __( 'There was an error attempting to get the region of the bucket %s: %s', 'as3cf' ), $bucket, $e->getMessage() );
1456
  error_log( $error_msg );
1457
 
1458
- return new WP_Error( 'exception', $e->getMessage() );
1459
  }
1460
 
1461
  $region = $this->translate_region( $region['Location'] );
@@ -1508,7 +1598,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1508
  $region = strtolower( $region );
1509
 
1510
  switch ( $region ) {
1511
- case 'eu' :
1512
  $region = 'eu-west-1';
1513
  break;
1514
  }
@@ -1924,7 +2014,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1924
  * Apply ACL to an attachment and associated files
1925
  *
1926
  * @param int $post_id
1927
- * @param object $s3object
1928
  * @param string $acl
1929
  */
1930
  function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) {
@@ -2144,6 +2234,21 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2144
  }
2145
  echo "\r\n\r\n";
2146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2147
  echo 'Bucket: ';
2148
  echo $this->get_setting( 'bucket' );
2149
  echo "\r\n";
@@ -2215,7 +2320,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2215
  /**
2216
  * Helper for displaying settings
2217
  *
2218
- * @param $key setting key
2219
  *
2220
  * @return string
2221
  */
@@ -2228,7 +2333,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2228
  /**
2229
  * Helper to display plugin details
2230
  *
2231
- * @param $plugin_path
2232
  * @param string $suffix
2233
  */
2234
  function print_plugin_details( $plugin_path, $suffix = '' ) {
@@ -2317,6 +2422,32 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2317
  return false;
2318
  }
2319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2320
  /**
2321
  * Get all the table prefixes for the blogs in the site. MS compatible
2322
  *
@@ -2437,4 +2568,125 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2437
 
2438
  return $message;
2439
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2440
  }
43
  protected $default_tab = '';
44
 
45
  /**
46
+ * @var string
47
  */
48
  public $hook_suffix;
49
 
84
  $this->plugin_title = __( 'Offload S3', 'as3cf' );
85
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
86
 
87
+ new AS3CF_Upgrade_Region_Meta( $this );
88
+ new AS3CF_Upgrade_File_Sizes( $this );
89
 
90
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
91
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
107
  add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
108
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
109
  add_filter( 'plugin_action_links', array( $this, 'plugin_actions_settings_link' ), 10, 2 );
110
+ add_filter( 'pre_get_space_used', array( $this, 'multisite_get_spaced_used' ) );
111
 
112
  // include compatibility code for other plugins
113
  new AS3CF_Plugin_Compatibility( $this );
489
  }
490
 
491
  // upload attachment to S3
492
+ $data = $this->upload_attachment_to_s3( $post_id, $data );
493
 
494
  return $data;
495
  }
504
  * to cope with possible different regions
505
  * @param bool $remove_local_files
506
  *
507
+ * @return array|WP_Error $s3object|$meta If meta is supplied, return it. Else return S3 meta
508
  */
509
  function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $force_new_s3_client = false, $remove_local_files = true ) {
510
+ $return_metadata = true;
511
  if ( is_null( $data ) ) {
512
+ $return_metadata = false;
513
  $data = wp_get_attachment_metadata( $post_id, true );
514
  }
515
 
619
  $file_paths = $this->get_attachment_file_paths( $post_id, true, $data );
620
  $additional_images = array();
621
 
622
+ $filesize_total = 0;
623
+ $remove_local_files_setting = $this->get_setting( 'remove-local-file' );
624
+
625
+ if ( $remove_local_files_setting ) {
626
+ $bytes = filesize( $file_path );
627
+ if ( false !== $bytes ) {
628
+ // Store in the attachment meta data for use by WP
629
+ $data['filesize'] = $bytes;
630
+
631
+ if ( ! $return_metadata ) {
632
+ // Upload happening outside of 'wp_update_attachment_metadata' filter,
633
+ // So update metadata manually
634
+ update_post_meta( $post_id, '_wp_attachment_metadata', $data );
635
+ }
636
+
637
+ // Add to the file size total
638
+ $filesize_total += $bytes;
639
+ }
640
+ }
641
+
642
  foreach ( $file_paths as $file_path ) {
643
  if ( ! in_array( $file_path, $files_to_remove ) ) {
644
  $additional_images[] = array(
647
  );
648
 
649
  $files_to_remove[] = $file_path;
650
+
651
+ if ( $remove_local_files_setting ) {
652
+ // Record the file size for the additional image
653
+ $bytes = filesize( $file_path );
654
+ if ( false !== $bytes ) {
655
+ $filesize_total += $bytes;
656
+ }
657
+ }
658
  }
659
  }
660
 
670
  }
671
 
672
  if ( $remove_local_files ) {
673
+ if ( $remove_local_files_setting ) {
674
+ // Allow other functions to remove files after they have processed
675
+ $files_to_remove = apply_filters( 'as3cf_upload_attachment_local_files_to_remove', $files_to_remove, $post_id, $file_path );
676
+ // Remove duplicates
677
+ $files_to_remove = array_unique( $files_to_remove );
678
+
679
+ // Delete the files
 
 
 
680
  $this->remove_local_files( $files_to_remove );
681
  }
682
  }
683
 
684
+ // Store the file size in the attachment meta if we are removing local file
685
+ if ( $remove_local_files_setting ) {
686
+ if ( $filesize_total > 0 ) {
687
+ // Add the total file size for all image sizes
688
+ update_post_meta( $post_id, 'wpos3_filesize_total', $filesize_total );
689
+ }
690
+ } else {
691
+ if ( isset( $data['filesize'] ) ) {
692
+ // Make sure we don't have a cached file sizes in the meta
693
+ unset( $data['filesize'] );
694
+
695
+ if ( ! $return_metadata ) {
696
+ // Upload happening outside of 'wp_update_attachment_metadata' filter,
697
+ // So update metadata manually
698
+ update_post_meta( $post_id, '_wp_attachment_metadata', $data );
699
+ }
700
+
701
+ delete_post_meta( $post_id, 'wpos3_filesize_total' );
702
+ }
703
+ }
704
+
705
+ if ( $return_metadata ) {
706
+ // If the attachment metadata is supplied, return it
707
+ return $data;
708
+ }
709
+
710
  return $s3object;
711
  }
712
 
730
  return $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $hidpi_suffix . '.' . $pathinfo['extension'];
731
  }
732
 
733
+ /**
734
+ * Get the object versioning string prefix
735
+ *
736
+ * @param int $post_id
737
+ *
738
+ * @return string
739
+ */
740
  function get_object_version_string( $post_id ) {
741
  if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
742
  $date_format = 'dHis';
769
  return null;
770
  }
771
 
772
+ /**
773
+ * Get the time of attachment upload.
774
+ *
775
+ * Use post datetime if attached.
776
+ *
777
+ * @param int $post_id
778
+ *
779
+ * @return int|string
780
+ */
781
  function get_attachment_folder_time( $post_id ) {
782
  $time = current_time( 'timestamp' );
783
 
883
  return get_post_meta( $post_id, 'amazonS3_info', true );
884
  }
885
 
886
+ /**
887
+ * Check the plugin is correctly setup
888
+ *
889
+ * @return bool
890
+ */
891
  function is_plugin_setup() {
892
+ if ( is_wp_error( $this->aws->get_client() ) ) {
893
+ // AWS not configured
894
+ return false;
895
+ }
896
+
897
+ if ( false === (bool) $this->get_setting( 'bucket' ) ) {
898
+ // No bucket selected
899
+ return false;
900
+ }
901
+
902
+ if ( is_wp_error( $this->get_setting( 'region' ) ) ) {
903
+ // Region error when retrieving bucket location
904
+ return false;
905
+ }
906
+
907
+ // All good, let's do this
908
+ return true;
909
  }
910
 
911
  /**
912
  * Generate a link to download a file from Amazon S3 using query string
913
  * authentication. This link is only valid for a limited amount of time.
914
  *
915
+ * @param int $post_id Post ID of the attachment
916
+ * @param int|null $expires Seconds for the link to live
917
+ * @param string|null $size Size of the image to get
918
+ * @param array $headers Header overrides for request
919
  *
920
  * @return mixed|void|WP_Error
921
  */
1070
  /**
1071
  * Get the url of the file from Amazon S3
1072
  *
1073
+ * @param int $post_id Post ID of the attachment
1074
+ * @param int|null $expires Seconds for the link to live
1075
+ * @param string|null $size Size of the image to get
1076
+ * @param array|null $meta Pre retrieved _wp_attachment_metadata for the attachment
1077
+ * @param array $headers Header overrides for request
1078
  *
1079
  * @return bool|mixed|void|WP_Error
1080
  */
1105
 
1106
  $domain_bucket = $this->get_s3_url_domain( $s3object['bucket'], $region, $expires );
1107
 
1108
+ if ( ! is_null( $size ) ) {
1109
  if ( is_null( $meta ) ) {
1110
  $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
1111
  }
1116
 
1117
  if ( ! is_null( $expires ) ) {
1118
  try {
1119
+ $expires = time() + $expires;
1120
  $secure_url = $this->get_s3client( $region )->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires, $headers );
1121
+
1122
+ return apply_filters( 'as3cf_get_attachment_secure_url', $secure_url, $s3object, $post_id, $expires, $headers );
1123
  }
1124
  catch ( Exception $e ) {
1125
  return new WP_Error( 'exception', $e->getMessage() );
1126
  }
1127
  }
1128
 
 
1129
  $file = $this->encode_filename_in_path( $s3object['key'] );
1130
+ $url = $scheme . '://' . $domain_bucket . '/' . $file;
1131
 
1132
+ return apply_filters( 'as3cf_get_attachment_url', $url, $s3object, $post_id, $expires, $headers );
 
 
 
 
 
1133
  }
1134
 
1135
  /**
1496
  * @param Amazon_Web_Services $aws
1497
  */
1498
  function admin_menu( $aws ) {
1499
+ $hook_suffix = $aws->add_page( $this->get_plugin_page_title(), $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
1500
+
1501
+ if ( false !== $hook_suffix ) {
1502
+ $this->hook_suffix = $hook_suffix;
1503
+ add_action( 'load-' . $this->hook_suffix, array( $this, 'plugin_load' ) );
1504
+ }
1505
  }
1506
 
1507
  /**
1540
  function get_bucket_region( $bucket ) {
1541
  try {
1542
  $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
1543
+ } catch ( Exception $e ) {
1544
+ $error_msg_title = '<strong>' . __( 'Error Getting Bucket Region', 'as3cf' ) . '</strong> &mdash;';
1545
+ $error_msg = sprintf( __( 'There was an error attempting to get the region of the bucket %s: %s', 'as3cf' ), $bucket, $e->getMessage() );
1546
  error_log( $error_msg );
1547
 
1548
+ return new WP_Error( 'exception', $error_msg_title . $error_msg );
1549
  }
1550
 
1551
  $region = $this->translate_region( $region['Location'] );
1598
  $region = strtolower( $region );
1599
 
1600
  switch ( $region ) {
1601
+ case 'eu':
1602
  $region = 'eu-west-1';
1603
  break;
1604
  }
2014
  * Apply ACL to an attachment and associated files
2015
  *
2016
  * @param int $post_id
2017
+ * @param array $s3object
2018
  * @param string $acl
2019
  */
2020
  function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) {
2234
  }
2235
  echo "\r\n\r\n";
2236
 
2237
+ $media_counts = $this->diagnostic_media_counts();
2238
+
2239
+ echo 'Media Files: ';
2240
+ echo number_format_i18n( $media_counts['all'] );
2241
+ echo "\r\n";
2242
+
2243
+ echo 'Media Files on S3: ';
2244
+ echo number_format_i18n( $media_counts['s3'] );
2245
+ echo "\r\n";
2246
+
2247
+ echo 'Number of Image Sizes: ';
2248
+ $sizes = count( get_intermediate_image_sizes() );
2249
+ echo number_format_i18n( $sizes );
2250
+ echo "\r\n\r\n";
2251
+
2252
  echo 'Bucket: ';
2253
  echo $this->get_setting( 'bucket' );
2254
  echo "\r\n";
2320
  /**
2321
  * Helper for displaying settings
2322
  *
2323
+ * @param string $key setting key
2324
  *
2325
  * @return string
2326
  */
2333
  /**
2334
  * Helper to display plugin details
2335
  *
2336
+ * @param string $plugin_path
2337
  * @param string $suffix
2338
  */
2339
  function print_plugin_details( $plugin_path, $suffix = '' ) {
2422
  return false;
2423
  }
2424
 
2425
+ /**
2426
+ * Helper to switch to a Multisite blog
2427
+ * - If the site is MS
2428
+ * - If the blog is not the current blog defined
2429
+ *
2430
+ * @param $blog_id
2431
+ */
2432
+ function switch_to_blog( $blog_id ) {
2433
+ if ( is_multisite() && ! $this->is_current_blog( $blog_id ) ) {
2434
+ switch_to_blog( $blog_id );
2435
+ }
2436
+ }
2437
+
2438
+ /**
2439
+ * Helper to restore to the current Multisite blog
2440
+ * - If the site is MS
2441
+ * - If the blog is not the current blog defined
2442
+ *
2443
+ * @param $blog_id
2444
+ */
2445
+ function restore_current_blog( $blog_id ) {
2446
+ if ( is_multisite() && ! $this->is_current_blog( $blog_id ) ) {
2447
+ restore_current_blog();
2448
+ }
2449
+ }
2450
+
2451
  /**
2452
  * Get all the table prefixes for the blogs in the site. MS compatible
2453
  *
2568
 
2569
  return $message;
2570
  }
2571
+
2572
+ /**
2573
+ * Used to give a realistic total of storage space used on a Multisite subsite,
2574
+ * when there have been attachments uploaded to S3 but removed from server
2575
+ *
2576
+ * @param $space_used bool
2577
+ *
2578
+ * @return float|int
2579
+ */
2580
+ function multisite_get_spaced_used( $space_used ) {
2581
+ if ( false === ( $space_used = get_transient( 'wpos3_site_space_used' ) ) ) {
2582
+ global $wpdb;
2583
+
2584
+ // Sum the total file size (including image sizes) for all S3 attachments
2585
+ $sql = "SELECT SUM( meta_value ) AS bytes_total
2586
+ FROM {$wpdb->postmeta}
2587
+ WHERE meta_key = 'wpos3_filesize_total'";
2588
+
2589
+ $space_used = $wpdb->get_var( $sql );
2590
+
2591
+ // Get local upload sizes
2592
+ $upload_dir = wp_upload_dir();
2593
+ $space_used += get_dirsize( $upload_dir['basedir'] );
2594
+
2595
+ if ( $space_used > 0 ) {
2596
+ // Convert to bytes to MB
2597
+ $space_used = $space_used / 1024 / 1024;
2598
+ }
2599
+
2600
+ set_transient( 'wpos3_site_space_used', $space_used, HOUR_IN_SECONDS );
2601
+ }
2602
+
2603
+ return $space_used;
2604
+ }
2605
+
2606
+ /**
2607
+ * Memory exceeded
2608
+ *
2609
+ * Ensures the a process never exceeds 90% of the maximum WordPress memory.
2610
+ *
2611
+ * @param null|string $filter_name Name of filter to apply to the return
2612
+ *
2613
+ * @return bool
2614
+ */
2615
+ public function memory_exceeded( $filter_name = null ) {
2616
+ $current_memory = memory_get_usage( true );
2617
+ $memory_limit = ( intval( WP_MEMORY_LIMIT ) * 1024 * 1024 ) * 0.9; // 90% of max memory
2618
+ $return = false;
2619
+
2620
+ if ( $current_memory >= $memory_limit ) {
2621
+ $return = true;
2622
+ }
2623
+
2624
+ if ( is_null( $filter_name ) || ! is_string( $filter_name ) ) {
2625
+ return $return;
2626
+ }
2627
+
2628
+ return apply_filters( $filter_name, $return );
2629
+ }
2630
+
2631
+ /**
2632
+ * Count attachments on a site
2633
+ *
2634
+ * @param string $prefix
2635
+ * @param null|bool $uploaded_to_s3
2636
+ * null - All attachments
2637
+ * true - Attachments only uploaded to S3
2638
+ * false - Attachments not uploaded to S3
2639
+ *
2640
+ * @return null|string
2641
+ */
2642
+ public function count_attachments( $prefix, $uploaded_to_s3 = null ) {
2643
+ global $wpdb;
2644
+
2645
+ $sql = "SELECT COUNT(*)
2646
+ FROM `{$prefix}posts` p";
2647
+
2648
+ $where = "WHERE p.post_type = 'attachment'";
2649
+
2650
+ if ( ! is_null( $uploaded_to_s3 ) && is_bool( $uploaded_to_s3 ) ) {
2651
+ $sql .= " LEFT OUTER JOIN `{$prefix}postmeta` pm
2652
+ ON p.`ID` = pm.`post_id`
2653
+ AND pm.`meta_key` = 'amazonS3_info'";
2654
+
2655
+ $operator = $uploaded_to_s3 ? 'not ' : '';
2656
+ $where .= " AND pm.`post_id` is {$operator}null";
2657
+ }
2658
+
2659
+ $sql .= ' ' . $where;
2660
+
2661
+ return $wpdb->get_var( $sql );
2662
+ }
2663
+
2664
+ /**
2665
+ * Get the total attachment and total S3 attachment counts for the diagnostic log
2666
+ *
2667
+ * @return array
2668
+ */
2669
+ protected function diagnostic_media_counts() {
2670
+ if ( false === ( $attachment_counts = get_site_transient( 'wpos3_attachment_counts' ) ) ) {
2671
+ $table_prefixes = $this->get_all_blog_table_prefixes();
2672
+ $all_media = 0;
2673
+ $all_media_s3 = 0;
2674
+
2675
+ foreach ( $table_prefixes as $blog_id => $table_prefix ) {
2676
+ $count = $this->count_attachments( $table_prefix );
2677
+ $all_media += $count;
2678
+ $s3_count = $this->count_attachments( $table_prefix, true );
2679
+ $all_media_s3 += $s3_count;
2680
+ }
2681
+
2682
+ $attachment_counts = array(
2683
+ 'all' => $all_media,
2684
+ 's3' => $all_media_s3,
2685
+ );
2686
+
2687
+ set_site_transient( 'wpos3_attachment_counts', $attachment_counts, 2 * HOUR_IN_SECONDS );
2688
+ }
2689
+
2690
+ return $attachment_counts;
2691
+ }
2692
  }
classes/as3cf-plugin-compatibility.php CHANGED
@@ -50,13 +50,15 @@ class AS3CF_Plugin_Compatibility {
50
  */
51
  add_action( 'as3cf_upload_attachment_pre_remove', array( $this, 'image_editor_remove_files' ), 10, 4 );
52
  add_filter( 'as3cf_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
 
 
 
53
 
54
  /*
55
  * WP_Customize_Control
56
  * /wp-includes/class-wp-customize_control.php
57
  */
58
  add_filter( 'attachment_url_to_postid', array( $this, 'customizer_background_image' ), 10, 2 );
59
-
60
  /*
61
  * Regenerate Thumbnails
62
  * https://wordpress.org/plugins/regenerate-thumbnails/
@@ -91,6 +93,25 @@ class AS3CF_Plugin_Compatibility {
91
  return $url;
92
  }
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  /**
95
  * Allow the WordPress Image Editor to remove edited version of images
96
  * if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
@@ -144,9 +165,9 @@ class AS3CF_Plugin_Compatibility {
144
  $this->copy_s3_file_to_server( $orig_s3, $orig_file );
145
 
146
  // Copy the edited file back to the server as well, it will be cleaned up later
147
- if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
148
  // Return the file if successfully downloaded from S3
149
- return $file;
150
  };
151
  }
152
 
@@ -156,9 +177,9 @@ class AS3CF_Plugin_Compatibility {
156
  foreach ( $callers as $caller ) {
157
  if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
158
  // check this has been called by '_load_image_to_edit_path' so as only to copy back once
159
- if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
160
  // Return the file if successfully downloaded from S3
161
- return $file;
162
  };
163
  }
164
  }
@@ -167,6 +188,82 @@ class AS3CF_Plugin_Compatibility {
167
  return $url;
168
  }
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  /**
171
  * Show the correct background image in the customizer
172
  *
50
  */
51
  add_action( 'as3cf_upload_attachment_pre_remove', array( $this, 'image_editor_remove_files' ), 10, 4 );
52
  add_filter( 'as3cf_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
53
+ add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'image_editor_remove_original_image' ), 10, 3 );
54
+ add_filter( 'as3cf_get_attached_file', array( $this, 'customizer_header_crop_download_file' ), 10, 4 );
55
+ add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'customizer_header_crop_remove_original_image' ), 10, 3 );
56
 
57
  /*
58
  * WP_Customize_Control
59
  * /wp-includes/class-wp-customize_control.php
60
  */
61
  add_filter( 'attachment_url_to_postid', array( $this, 'customizer_background_image' ), 10, 2 );
 
62
  /*
63
  * Regenerate Thumbnails
64
  * https://wordpress.org/plugins/regenerate-thumbnails/
93
  return $url;
94
  }
95
 
96
+ /**
97
+ * Get the file path of the original image file before an update
98
+ *
99
+ * @param int $post_id
100
+ * @param string $file_path
101
+ *
102
+ * @return bool|string
103
+ */
104
+ function get_original_image_file( $post_id, $file_path ) {
105
+ // remove original main image after edit
106
+ $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
107
+ $original_file = trailingslashit( dirname( $file_path ) ) . basename( $meta['file'] );
108
+ if ( file_exists( $original_file ) ) {
109
+ return $original_file;
110
+ }
111
+
112
+ return false;
113
+ }
114
+
115
  /**
116
  * Allow the WordPress Image Editor to remove edited version of images
117
  * if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
165
  $this->copy_s3_file_to_server( $orig_s3, $orig_file );
166
 
167
  // Copy the edited file back to the server as well, it will be cleaned up later
168
+ if ( ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
169
  // Return the file if successfully downloaded from S3
170
+ return $s3_file;
171
  };
172
  }
173
 
177
  foreach ( $callers as $caller ) {
178
  if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
179
  // check this has been called by '_load_image_to_edit_path' so as only to copy back once
180
+ if ( ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
181
  // Return the file if successfully downloaded from S3
182
+ return $s3_file;
183
  };
184
  }
185
  }
188
  return $url;
189
  }
190
 
191
+ /**
192
+ * Allow the WordPress Image Editor to remove the main image file after it has been copied
193
+ * back from S3 after it has done the edit.
194
+ *
195
+ * @param array $files
196
+ * @param int $post_id
197
+ * @param string $file_path
198
+ *
199
+ * @return array
200
+ */
201
+ function image_editor_remove_original_image( $files, $post_id, $file_path ) {
202
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
203
+ return $files;
204
+ }
205
+
206
+ if ( isset( $_POST['action'] ) && 'image-editor' === sanitize_key( $_POST['action'] ) ) { // input var okay
207
+ // remove original main image after edit
208
+ if ( ( $original_file = $this->get_original_image_file( $post_id, $file_path ) ) ) {
209
+ $files[] = $original_file;
210
+ }
211
+ }
212
+
213
+ return $files;
214
+ }
215
+
216
+ /**
217
+ * Allow the WordPress Customizer to crop images that have been copied to S3
218
+ * but removed from the local server, by copying them back temporarily
219
+ *
220
+ * @param string $url
221
+ * @param string $file
222
+ * @param int $attachment_id
223
+ * @param array $s3_object
224
+ *
225
+ * @return string
226
+ */
227
+ function customizer_header_crop_download_file( $url, $file, $attachment_id, $s3_object ) {
228
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
229
+ return $url;
230
+ }
231
+
232
+ if ( isset( $_POST['action'] ) && 'custom-header-crop' === sanitize_key( $_POST['action'] ) ) { // input var okay
233
+ if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
234
+ // Return the file if successfully downloaded from S3
235
+ return $file;
236
+ };
237
+ }
238
+
239
+ return $url;
240
+ }
241
+
242
+ /**
243
+ * Allow the WordPress Image Editor to remove the main image file after it has been copied
244
+ * back from S3 after it has done the edit.
245
+ *
246
+ * @param array $files
247
+ * @param int $post_id
248
+ * @param string $file_path
249
+ *
250
+ * @return array
251
+ */
252
+ function customizer_header_crop_remove_original_image( $files, $post_id, $file_path ) {
253
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
254
+ return $files;
255
+ }
256
+
257
+ if ( isset( $_POST['action'] ) && 'custom-header-crop' === sanitize_key( $_POST['action'] ) ) { // input var okay
258
+ // remove original main image after edit
259
+ if ( ( $original_file = $this->get_original_image_file( $_POST['id'], $file_path ) ) ) {
260
+ $files[] = $original_file;
261
+ }
262
+ }
263
+
264
+ return $files;
265
+ }
266
+
267
  /**
268
  * Show the correct background image in the customizer
269
  *
classes/as3cf-upgrade.php CHANGED
@@ -17,18 +17,66 @@ if ( ! defined( 'ABSPATH' ) ) {
17
  /**
18
  * AS3CF_Upgrade Class
19
  *
20
- * This class handles data updates and other migrations after a plugin update
21
  *
22
  * @since 0.6.2
23
  */
24
- class AS3CF_Upgrade {
25
 
26
- private $as3cf;
27
- private $cron_interval_in_minutes;
28
- private $error_threshold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- const CRON_HOOK = 'as3cf_cron_update_meta_with_region';
31
- const CRON_SCHEDULE_KEY = 'as3cf_update_meta_with_region_interval';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  const STATUS_RUNNING = 1;
34
  const STATUS_ERROR = 2;
@@ -42,169 +90,122 @@ class AS3CF_Upgrade {
42
  function __construct( $as3cf ) {
43
  $this->as3cf = $as3cf;
44
 
45
- $this->cron_interval_in_minutes = apply_filters( 'as3cf_update_meta_with_region_interval', 10 );
46
- $this->error_threshold = apply_filters( 'as3cf_update_meta_with_region_error_threshold', 20 );
 
 
 
47
 
48
  add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
49
- add_action( self::CRON_HOOK, array( $this, 'cron_update_meta_with_region' ) );
50
 
51
  add_action( 'as3cf_pre_settings_render', array( $this, 'maybe_display_notices' ) );
52
  add_action( 'admin_init', array( $this, 'maybe_handle_action' ) );
53
 
54
- $this->maybe_init_upgrade();
 
 
 
55
  }
56
 
57
  /**
58
- * Maybe initialize the upgrade
 
 
59
  */
60
- function maybe_init_upgrade() {
61
  if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
62
- return;
63
  }
64
 
65
  // make sure this only fires inside the network admin for multisites
66
  if ( is_multisite() && ! is_network_admin() ) {
67
- return;
68
- }
69
-
70
- // Have we completed the upgrade yet?
71
- if ( $this->as3cf->get_setting( 'post_meta_version', 0 ) > 0 ) {
72
- return;
73
  }
74
 
75
  // If the upgrade status is already set, then we've already initialized the upgrade
76
  if ( $this->get_upgrade_status() ) {
77
- return;
78
  }
79
 
80
- // Do we actually have S3 meta data without regions to update?
81
- // No need to bother for fresh sites, or media not uploaded to S3
82
- if ( 0 == $this->count_all_attachments_without_region() ) {
83
- $this->as3cf->set_setting( 'post_meta_version', 1 );
84
- $this->as3cf->save_settings();
85
-
86
- return;
87
  }
88
 
89
- // Initialize the upgrade
90
- $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
91
-
92
- $this->as3cf->schedule_event( self::CRON_HOOK, self::CRON_SCHEDULE_KEY );
93
- }
94
-
95
- /**
96
- * Adds notices about issues with upgrades allowing user to restart them
97
- */
98
- function maybe_display_notices() {
99
- $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'restart_update_meta_with_region' ), 'self' );
100
- $msg_type = 'notice-info';
101
-
102
- switch ( $this->get_upgrade_status() ) {
103
- case self::STATUS_RUNNING :
104
- $msg = sprintf( __( '<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 and updating the metadata with the bucket region it is served from. This will allow us to serve your files from the proper S3 region subdomain <span style="white-space:nowrap;">(e.g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the background, processing a small batch of Media Library items every %d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'as3cf' ), $this->cron_interval_in_minutes );
105
- $action_text = __( 'Pause Update', 'as3cf' );
106
- $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'pause_update_meta_with_region' ), 'self' );
107
- break;
108
- case self::STATUS_PAUSED :
109
- $msg = __( '<strong>Metadata Update Paused</strong> &mdash; Updating Media Library metadata has been paused.', 'as3cf' );
110
- $action_text = __( 'Restart Update', 'as3cf' );
111
- break;
112
- case self::STATUS_ERROR :
113
- $msg = __( '<strong>Error Updating Metadata</strong> &mdash; We ran into some errors attempting to update the metadata for all your Media Library items that have been uploaded to S3. Please check your error log for details.', 'as3cf' );
114
- $action_text = __( 'Try Run It Again', 'as3cf' );
115
- $msg_type = 'error';
116
- break;
117
- default :
118
- return;
119
  }
120
 
121
- $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
122
-
123
- $args = array(
124
- 'message' => $msg,
125
- 'type' => $msg_type,
126
- );
127
 
128
- $this->as3cf->render_view( 'notice', $args );
129
- }
130
-
131
- function maybe_handle_action() {
132
- if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) != $this->as3cf->get_plugin_slug() || ! isset( $_GET['action'] ) ) { // input var okay
133
- return;
134
  }
135
 
136
- $method_name = 'action_' . sanitize_key( $_GET['action'] ); // input var okay
137
- if ( method_exists( $this, $method_name ) ) {
138
- call_user_func( array( $this, $method_name ) );
139
- }
140
  }
141
 
142
  /**
143
- * Restart upgrade
144
  */
145
- function action_restart_update_meta_with_region() {
146
- $this->change_status_request( self::STATUS_RUNNING );
147
- $this->as3cf->schedule_event( self::CRON_HOOK, self::CRON_SCHEDULE_KEY );
148
- }
149
 
150
  /**
151
- * Pause upgrade
 
 
 
152
  */
153
- function action_pause_update_meta_with_region() {
154
- $this->clear_scheduled_event();
155
- $this->change_status_request( self::STATUS_PAUSED );
156
- }
157
 
158
  /**
159
- * Helper for the above action requests
160
  *
161
- * @param integer $status
162
  */
163
- function change_status_request( $status ) {
164
- $session = $this->get_session();
165
- $session['status'] = $status;
166
- $this->save_session( $session );
167
-
168
- $url = $this->as3cf->get_plugin_page_url( array(), 'self' );
169
- wp_redirect( $url );
170
- }
171
 
172
  /**
173
- * Add custom cron interval schedules
174
- *
175
- * @param array $schedules
176
- *
177
- * @return array
178
  */
179
- function cron_schedules( $schedules ) {
180
- // Adds every 10 minutes to the existing schedules.
181
- $schedules[ self::CRON_SCHEDULE_KEY ] = array(
182
- 'interval' => $this->cron_interval_in_minutes * 60,
183
- 'display' => sprintf( __( 'Every %d Minutes', 'as3cf' ), $this->cron_interval_in_minutes ),
184
- );
185
 
186
- return $schedules;
187
  }
188
-
189
  /**
190
  * Cron jon to update the region of the bucket in s3 metadata
191
  */
192
- function cron_update_meta_with_region() {
193
  // Check if the cron should even be running
194
- if ( $this->as3cf->get_setting( 'post_meta_version', 0 ) > 0 || $this->get_upgrade_status() != self::STATUS_RUNNING ) {
195
- $this->as3cf->clear_scheduled_event( self::CRON_HOOK );
 
196
  return;
197
  }
198
 
199
  // set the batch size limit for the query
200
- $limit = apply_filters( 'as3cf_update_meta_with_region_batch_size', 500 );
201
  $all_limit = $limit;
202
 
203
- $session = $this->get_session();
 
 
 
 
 
 
204
 
205
  // find the blog IDs that have been processed so we can skip them
206
  $processed_blog_ids = isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
207
- $error_count = isset( $session['error_count'] ) ? $session['error_count'] : 0;
208
 
209
  // get the table prefixes for all the blogs
210
  $table_prefixes = $this->as3cf->get_all_blog_table_prefixes( $processed_blog_ids );
@@ -213,10 +214,10 @@ class AS3CF_Upgrade {
213
  $all_count = 0;
214
 
215
  foreach ( $table_prefixes as $blog_id => $table_prefix ) {
216
- $attachments = $this->get_attachments_without_region( $table_prefix, $limit );
217
  $count = count( $attachments );
218
 
219
- if ( 0 == $count ) {
220
  // no more attachments, record the blog ID to skip next time
221
  $processed_blog_ids[] = $blog_id;
222
  } else {
@@ -231,204 +232,205 @@ class AS3CF_Upgrade {
231
  $limit = $limit - $count;
232
  }
233
 
234
- if ( 0 == $all_count ) {
235
- $this->as3cf->set_setting( 'post_meta_version', 1 );
236
- $this->as3cf->remove_setting( 'update_meta_with_region_session' );
237
- $this->as3cf->save_settings();
238
- $this->as3cf->clear_scheduled_event( self::CRON_HOOK );
239
  return;
240
  }
241
 
242
- // only process the loop for a certain amount of time
243
- $minutes = $this->cron_interval_in_minutes * 60;
244
-
245
- // smaller time limit so won't run into another instance of cron
246
- $minutes = $minutes * 0.8;
247
-
248
- $finish = time() + $minutes;
249
-
250
  // loop through and update s3 meta with region
251
  foreach ( $all_attachments as $blog_id => $attachments ) {
252
- if ( is_multisite() && ! $this->as3cf->is_current_blog( $blog_id ) ) {
253
- switch_to_blog( $blog_id );
254
- }
255
 
256
  foreach ( $attachments as $attachment ) {
257
- if ( $error_count >= $this->error_threshold ) {
258
- $session['status'] = self::STATUS_ERROR;
259
- $this->save_session( $session );
260
- $this->clear_scheduled_event();
261
- return;
262
- }
263
 
264
- if ( time() >= $finish ) {
265
- break;
266
  }
267
 
268
- $s3object = unserialize( $attachment->s3object );
269
- if ( false === $s3object ) {
270
- error_log( 'Failed to unserialize S3 meta for attachment ' . $attachment->ID . ': ' . $attachment->s3object );
271
- $error_count++;
272
- continue;
273
- }
274
 
275
- // retrieve region and update the attachment metadata
276
- $region = $this->as3cf->get_s3object_region( $s3object, $attachment->ID );
277
- if ( is_wp_error( $region ) ) {
278
- error_log( 'Error updating region: ' . $region->get_error_message() );
279
- $error_count++;
280
  }
281
  }
282
-
283
- if ( is_multisite() && ! $this->as3cf->is_current_blog( $blog_id ) ) {
284
- restore_current_blog();
285
- }
286
  }
287
 
 
 
288
  $session['processed_blog_ids'] = $processed_blog_ids;
289
- $session['error_count'] = $error_count;
290
 
291
  $this->save_session( $session );
292
  }
293
 
294
  /**
295
- * Get a count of all attachments without region in their S3 metadata
296
- * for the whole site
297
- *
298
- * @return int
299
  */
300
- function count_all_attachments_without_region() {
301
- // get the table prefixes for all the blogs
302
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
303
- $all_count = 0;
304
 
305
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
306
- $count = $this->count_attachments_without_region( $table_prefix );
307
- $all_count += $count;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  }
309
 
310
- return $all_count;
 
 
 
 
 
 
 
311
  }
312
 
313
  /**
314
- * Get the current status of the upgrade
315
- * See STATUS_* constants in the class declaration above.
316
  */
317
- function get_upgrade_status() {
318
- $session = $this->get_session();
319
-
320
- if ( ! isset( $session['status'] ) ) {
321
- return '';
322
  }
323
 
324
- return $session['status'];
 
 
 
325
  }
326
 
327
  /**
328
- * Retrieve session data from plugin settings
329
  *
330
- * @return array
331
  */
332
- function get_session() {
333
- return $this->as3cf->get_setting( 'update_meta_with_region_session', array() );
 
 
334
  }
335
 
336
  /**
337
- * Store data to be used between requests in plugin settings
338
- *
339
- * @param $session array of session data to store
340
  */
341
- function save_session( $session ) {
342
- $this->as3cf->set_setting( 'update_meta_with_region_session', $session );
 
343
  $this->as3cf->save_settings();
 
344
  }
345
 
346
  /**
347
- * Get all the table prefixes for the blogs in the site. MS compatible
348
- *
349
- * @param array $exclude_blog_ids blog ids to exclude
350
- *
351
- * @return array associative array with blog ID as key, prefix as value
352
  */
353
- function get_all_blog_table_prefixes( $exclude_blog_ids = array() ) {
354
- global $wpdb;
355
- $prefix = $wpdb->prefix;
356
-
357
- $table_prefixes = array();
358
-
359
- if ( ! in_array( 1, $exclude_blog_ids ) ) {
360
- $table_prefixes[1] = $prefix;
361
- }
362
-
363
- if ( is_multisite() ) {
364
- $blog_ids = $this->as3cf->get_blog_ids();
365
- foreach ( $blog_ids as $blog_id ) {
366
- if ( in_array( $blog_id, $exclude_blog_ids ) ) {
367
- continue;
368
- }
369
- $table_prefixes[ $blog_id ] = $wpdb->get_blog_prefix( $blog_id );
370
- }
371
  }
372
 
373
- return $table_prefixes;
 
374
  }
375
 
376
  /**
377
- * Get all attachments that don't have region in their S3 meta data for a blog
378
- *
379
- * @param string $prefix
380
- * @param int $limit
381
- *
382
- * @return mixed
383
  */
384
- function get_attachments_without_region( $prefix, $limit ) {
385
- $attachments = $this->get_attachments_without_region_results( $prefix, false, $limit );
 
 
386
 
387
- return $attachments;
 
388
  }
389
 
390
  /**
391
- * Get a count of attachments that don't have region in their S3 meta data for a blog
392
- * @param $prefix
393
  *
394
- * @return int
395
  */
396
- function count_attachments_without_region( $prefix ) {
397
- $count = $this->get_attachments_without_region_results( $prefix, true );
 
 
398
 
399
- return $count;
 
 
400
  }
401
 
402
  /**
403
- * Wrapper for database call to get attachments without region
404
  *
405
- * @param string $prefix
406
- * @param bool $count return count of attachments
407
- * @param null|int $limit
408
  *
409
- * @return mixed
410
  */
411
- function get_attachments_without_region_results( $prefix, $count = false, $limit = null ) {
412
- global $wpdb;
 
 
 
 
413
 
414
- $sql = " FROM `{$prefix}postmeta`
415
- WHERE `meta_key` = 'amazonS3_info'
416
- AND `meta_value` NOT LIKE '%%\"region\"%%'";
417
 
418
- if ( $count ) {
419
- $sql = 'SELECT COUNT(*)' . $sql;
 
 
 
 
420
 
421
- return $wpdb->get_var( $sql );
 
422
  }
423
 
424
- $sql = "SELECT `post_id` as `ID`, `meta_value` AS 's3object'" . $sql;
 
425
 
426
- if ( ! is_null( $limit ) ) {
427
- $sql .= ' LIMIT %d';
 
 
 
 
 
 
428
 
429
- $sql = $wpdb->prepare( $sql, $limit );
430
- }
 
 
 
 
 
 
431
 
432
- return $wpdb->get_results( $sql, OBJECT );
 
 
 
 
 
433
  }
434
  }
17
  /**
18
  * AS3CF_Upgrade Class
19
  *
20
+ * This class handles updates to attachments and attachment meta data
21
  *
22
  * @since 0.6.2
23
  */
24
+ abstract class AS3CF_Upgrade {
25
 
26
+ /**
27
+ * @var Amazon_S3_And_CloudFront
28
+ */
29
+ protected $as3cf;
30
+
31
+ /**
32
+ * @var int
33
+ */
34
+ protected $upgrade_id;
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ protected $upgrade_name;
40
+
41
+ /**
42
+ * @var string 'metadata', 'attachment'
43
+ */
44
+ protected $upgrade_type;
45
+
46
+ /**
47
+ * @var string
48
+ */
49
+ protected $running_update_text;
50
+
51
+ /**
52
+ * @var string
53
+ */
54
+ protected $settings_key = 'post_meta_version';
55
+
56
+ /**
57
+ * @var string
58
+ */
59
+ protected $cron_hook;
60
 
61
+ /**
62
+ * @var string
63
+ */
64
+ protected $cron_schedule_key;
65
+
66
+ /**
67
+ * @var mixed|void
68
+ */
69
+ protected $cron_interval_in_minutes;
70
+
71
+ /**
72
+ * @var mixed|void
73
+ */
74
+ protected $error_threshold;
75
+
76
+ /**
77
+ * @var int
78
+ */
79
+ protected $error_count;
80
 
81
  const STATUS_RUNNING = 1;
82
  const STATUS_ERROR = 2;
90
  function __construct( $as3cf ) {
91
  $this->as3cf = $as3cf;
92
 
93
+ $this->cron_hook = 'as3cf_cron_update_' . $this->upgrade_name;
94
+ $this->cron_schedule_key = 'as3cf_update_' . $this->upgrade_name . '_interval';
95
+
96
+ $this->cron_interval_in_minutes = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_interval', 5 );
97
+ $this->error_threshold = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_error_threshold', 20 );
98
 
99
  add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
100
+ add_action( $this->cron_hook, array( $this, 'do_upgrade' ) );
101
 
102
  add_action( 'as3cf_pre_settings_render', array( $this, 'maybe_display_notices' ) );
103
  add_action( 'admin_init', array( $this, 'maybe_handle_action' ) );
104
 
105
+ // Do default checks if the upgrade can be started
106
+ if ( $this->maybe_init() ) {
107
+ $this->init();
108
+ }
109
  }
110
 
111
  /**
112
+ * Can we start the upgrade using default checks
113
+ *
114
+ * @return bool
115
  */
116
+ protected function maybe_init() {
117
  if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
118
+ return false;
119
  }
120
 
121
  // make sure this only fires inside the network admin for multisites
122
  if ( is_multisite() && ! is_network_admin() ) {
123
+ return false;
 
 
 
 
 
124
  }
125
 
126
  // If the upgrade status is already set, then we've already initialized the upgrade
127
  if ( $this->get_upgrade_status() ) {
128
+ return false;
129
  }
130
 
131
+ // Have we completed the upgrade?
132
+ if ( $this->as3cf->get_setting( $this->settings_key, 0 ) >= $this->upgrade_id ) {
133
+ return false;
 
 
 
 
134
  }
135
 
136
+ // Has the previous upgrade completed yet?
137
+ $previous_id = $this->upgrade_id - 1;
138
+ if ( 0 !== $previous_id && (int) $this->as3cf->get_setting( $this->settings_key, 0 ) < $previous_id ) {
139
+ // Previous still running, abort
140
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  }
142
 
143
+ // Do we actually attachments to process?
144
+ if ( 0 === $this->count_attachments_to_process() ) {
145
+ $this->upgrade_finished();
 
 
 
146
 
147
+ return false;
 
 
 
 
 
148
  }
149
 
150
+ return true;
 
 
 
151
  }
152
 
153
  /**
154
+ * @return int
155
  */
156
+ abstract protected function count_attachments_to_process();
 
 
 
157
 
158
  /**
159
+ * @param $prefix
160
+ * @param $limit
161
+ *
162
+ * @return array
163
  */
164
+ abstract protected function get_attachments_to_process( $prefix, $limit );
 
 
 
165
 
166
  /**
167
+ * @param $attachment
168
  *
169
+ * @return bool
170
  */
171
+ abstract protected function upgrade_attachment( $attachment );
 
 
 
 
 
 
 
172
 
173
  /**
174
+ * Fire up the upgrade
 
 
 
 
175
  */
176
+ protected function init() {
177
+ // Initialize the upgrade
178
+ $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
 
 
 
179
 
180
+ $this->as3cf->schedule_event( $this->cron_hook, $this->cron_schedule_key );
181
  }
182
+
183
  /**
184
  * Cron jon to update the region of the bucket in s3 metadata
185
  */
186
+ function do_upgrade() {
187
  // Check if the cron should even be running
188
+ if ( $this->as3cf->get_setting( $this->settings_key, 0 ) >= $this->upgrade_id || $this->get_upgrade_status() !== self::STATUS_RUNNING ) {
189
+ $this->as3cf->clear_scheduled_event( $this->cron_hook );
190
+
191
  return;
192
  }
193
 
194
  // set the batch size limit for the query
195
+ $limit = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_batch_size', 500 );
196
  $all_limit = $limit;
197
 
198
+ // only process the loop for a certain amount of time
199
+ $minutes = $this->cron_interval_in_minutes * 60;
200
+ // smaller time limit so won't run into another instance of cron
201
+ $minutes = $minutes * 0.8;
202
+ $finish = time() + $minutes;
203
+
204
+ $session = $this->get_session();
205
 
206
  // find the blog IDs that have been processed so we can skip them
207
  $processed_blog_ids = isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
208
+ $this->error_count = isset( $session['error_count'] ) ? $session['error_count'] : 0;
209
 
210
  // get the table prefixes for all the blogs
211
  $table_prefixes = $this->as3cf->get_all_blog_table_prefixes( $processed_blog_ids );
214
  $all_count = 0;
215
 
216
  foreach ( $table_prefixes as $blog_id => $table_prefix ) {
217
+ $attachments = $this->get_attachments_to_process( $table_prefix, $limit );
218
  $count = count( $attachments );
219
 
220
+ if ( 0 === $count ) {
221
  // no more attachments, record the blog ID to skip next time
222
  $processed_blog_ids[] = $blog_id;
223
  } else {
232
  $limit = $limit - $count;
233
  }
234
 
235
+ if ( 0 === $all_count ) {
236
+ $this->upgrade_finished();
237
+
 
 
238
  return;
239
  }
240
 
 
 
 
 
 
 
 
 
241
  // loop through and update s3 meta with region
242
  foreach ( $all_attachments as $blog_id => $attachments ) {
243
+ $this->as3cf->switch_to_blog( $blog_id );
 
 
244
 
245
  foreach ( $attachments as $attachment ) {
246
+ if ( $this->error_count >= $this->error_threshold ) {
247
+ $this->upgrade_error( $session );
 
 
 
 
248
 
249
+ return;
 
250
  }
251
 
252
+ // Do the actual upgrade to the attachment
253
+ $this->upgrade_attachment( $attachment );
 
 
 
 
254
 
255
+ if ( time() >= $finish || $this->as3cf->memory_exceeded( 'as3cf_update_' . $this->upgrade_name . '_memory_exceeded' ) ) {
256
+ // Batch limits reached
257
+ break 2;
 
 
258
  }
259
  }
 
 
 
 
260
  }
261
 
262
+ $this->as3cf->restore_current_blog( $blog_id );
263
+
264
  $session['processed_blog_ids'] = $processed_blog_ids;
265
+ $session['error_count'] = $this->error_count;
266
 
267
  $this->save_session( $session );
268
  }
269
 
270
  /**
271
+ * Adds notices about issues with upgrades allowing user to restart them
 
 
 
272
  */
273
+ function maybe_display_notices() {
274
+ $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'restart_update', 'update' => $this->upgrade_name ), 'self' );
275
+ $msg_type = 'notice-info';
 
276
 
277
+ switch ( $this->get_upgrade_status() ) {
278
+ case self::STATUS_RUNNING:
279
+ $msg = sprintf( __( '<strong>Running %s Update</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 %s This will be done quietly in the background, processing a small batch of Media Library items every %d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'as3cf' ), ucfirst( $this->upgrade_type ), $this->running_update_text, $this->cron_interval_in_minutes );
280
+ $action_text = __( 'Pause Update', 'as3cf' );
281
+ $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'pause_update', 'update' => $this->upgrade_name ), 'self' );
282
+ break;
283
+ case self::STATUS_PAUSED:
284
+ $msg = sprintf( __( '<strong>%s Update Paused</strong> &mdash; Updating Media Library %s has been paused.', 'as3cf' ), ucfirst( $this->upgrade_type ), $this->upgrade_type );
285
+ $action_text = __( 'Restart Update', 'as3cf' );
286
+ break;
287
+ case self::STATUS_ERROR:
288
+ $msg = sprintf( __( '<strong>Error Updating %s</strong> &mdash; We ran into some errors attempting to update the %s for all your Media Library items that have been uploaded to S3. Please check your error log for details.', 'as3cf' ), ucfirst( $this->upgrade_type ), $this->upgrade_type );
289
+ $action_text = __( 'Try Run It Again', 'as3cf' );
290
+ $msg_type = 'error';
291
+ break;
292
+ default:
293
+ return;
294
  }
295
 
296
+ $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
297
+
298
+ $args = array(
299
+ 'message' => $msg,
300
+ 'type' => $msg_type,
301
+ );
302
+
303
+ $this->as3cf->render_view( 'notice', $args );
304
  }
305
 
306
  /**
307
+ * Handler for the running upgrade actions
 
308
  */
309
+ function maybe_handle_action() {
310
+ if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== $this->as3cf->get_plugin_slug() || ! isset( $_GET['action'] ) ) { // input var okay
311
+ return;
 
 
312
  }
313
 
314
+ $method_name = 'action_' . sanitize_key( $_GET['action'] ); // input var okay
315
+ if ( method_exists( $this, $method_name ) ) {
316
+ call_user_func( array( $this, $method_name ) );
317
+ }
318
  }
319
 
320
  /**
321
+ * Exit upgrade with an error
322
  *
323
+ * @param array $session
324
  */
325
+ function upgrade_error( $session ) {
326
+ $session['status'] = self::STATUS_ERROR;
327
+ $this->save_session( $session );
328
+ $this->as3cf->clear_scheduled_event( $this->cron_hook );
329
  }
330
 
331
  /**
332
+ * Complete the upgrade
 
 
333
  */
334
+ function upgrade_finished() {
335
+ $this->clear_session();
336
+ $this->as3cf->set_setting( $this->settings_key, $this->upgrade_id );
337
  $this->as3cf->save_settings();
338
+ $this->as3cf->clear_scheduled_event( $this->cron_hook );
339
  }
340
 
341
  /**
342
+ * Restart upgrade
 
 
 
 
343
  */
344
+ function action_restart_update() {
345
+ if ( ! isset( $_GET['update'] ) || $this->upgrade_name !== sanitize_key( $_GET['update'] ) ) {
346
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  }
348
 
349
+ $this->as3cf->schedule_event( $this->cron_hook, $this->cron_schedule_key );
350
+ $this->change_status_request( self::STATUS_RUNNING );
351
  }
352
 
353
  /**
354
+ * Pause upgrade
 
 
 
 
 
355
  */
356
+ function action_pause_update() {
357
+ if ( ! isset( $_GET['update'] ) || $this->upgrade_name !== sanitize_key( $_GET['update'] ) ) {
358
+ return;
359
+ }
360
 
361
+ $this->as3cf->clear_scheduled_event( $this->cron_hook );
362
+ $this->change_status_request( self::STATUS_PAUSED );
363
  }
364
 
365
  /**
366
+ * Helper for the above action requests
 
367
  *
368
+ * @param int $status
369
  */
370
+ function change_status_request( $status ) {
371
+ $session = $this->get_session();
372
+ $session['status'] = $status;
373
+ $this->save_session( $session );
374
 
375
+ $url = $this->as3cf->get_plugin_page_url( array(), 'self' );
376
+ wp_redirect( $url );
377
+ exit;
378
  }
379
 
380
  /**
381
+ * Add custom cron interval schedules
382
  *
383
+ * @param array $schedules
 
 
384
  *
385
+ * @return array
386
  */
387
+ function cron_schedules( $schedules ) {
388
+ // Add the upgrade interval to the existing schedules.
389
+ $schedules[ $this->cron_schedule_key ] = array(
390
+ 'interval' => $this->cron_interval_in_minutes * 60,
391
+ 'display' => sprintf( __( 'Every %d Minutes', 'as3cf' ), $this->cron_interval_in_minutes ),
392
+ );
393
 
394
+ return $schedules;
395
+ }
 
396
 
397
+ /**
398
+ * Get the current status of the upgrade
399
+ * See STATUS_* constants in the class declaration above.
400
+ */
401
+ function get_upgrade_status() {
402
+ $session = $this->get_session();
403
 
404
+ if ( ! isset( $session['status'] ) ) {
405
+ return '';
406
  }
407
 
408
+ return $session['status'];
409
+ }
410
 
411
+ /**
412
+ * Retrieve session data from plugin settings
413
+ *
414
+ * @return array
415
+ */
416
+ function get_session() {
417
+ return get_site_option( 'update_' . $this->upgrade_name . '_session', array() );
418
+ }
419
 
420
+ /**
421
+ * Store data to be used between requests in plugin settings
422
+ *
423
+ * @param array $session session data to store
424
+ */
425
+ function save_session( $session ) {
426
+ update_site_option( 'update_' . $this->upgrade_name . '_session', $session );
427
+ }
428
 
429
+ /**
430
+ * Remove the session data to be used between requests
431
+ *
432
+ */
433
+ function clear_session() {
434
+ delete_site_option( 'update_' . $this->upgrade_name . '_session' );
435
  }
436
  }
classes/upgrades/as3cf-file-sizes.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Update File Sizes
4
+ *
5
+ * @package amazon-s3-and-cloudfront
6
+ * @subpackage Classes/Upgrades/File-Sizes
7
+ * @copyright Copyright (c) 2015, Delicious Brains
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9.3
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * AS3CF_Upgrade_File_Sizes Class
19
+ *
20
+ * This class handles updating the file sizes in the meta data
21
+ * for attachments that have been removed from the local server
22
+ *
23
+ * @since 0.9.3
24
+ */
25
+ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
26
+
27
+ /**
28
+ * Initiate the upgrade
29
+ *
30
+ * @param object $as3cf Instance of calling class
31
+ */
32
+ public function __construct( $as3cf ) {
33
+ $this->upgrade_id = 2;
34
+ $this->upgrade_name = 'file_sizes';
35
+ $this->upgrade_type = 'attachments';
36
+
37
+ $this->running_update_text = __( 'and updating the metadata with the sizes of files that have been removed from the server. This will allow us to serve the correct size for media items and the total space used in Multisite subsites.', 'as3cf' );
38
+
39
+ parent::__construct( $as3cf );
40
+ }
41
+
42
+ /**
43
+ * Get the total file sizes for an attachment and associated files.
44
+ *
45
+ * @param $attachment
46
+ *
47
+ * @return bool
48
+ */
49
+ function upgrade_attachment( $attachment ) {
50
+ $s3object = unserialize( $attachment->s3object );
51
+ if ( false === $s3object ) {
52
+ error_log( 'Failed to unserialize S3 meta for attachment ' . $attachment->ID . ': ' . $attachment->s3object );
53
+ $this->error_count++;
54
+
55
+ return false;
56
+ }
57
+
58
+ $region = $this->as3cf->get_s3object_region( $s3object );
59
+ if ( is_wp_error( $region ) ) {
60
+ error_log( 'Failed to get the region for the bucket of the attachment ' . $attachment->ID );
61
+ $this->error_count++;
62
+
63
+ return false;
64
+ }
65
+
66
+ $s3client = $this->as3cf->get_s3client( $region, true );
67
+ $main_file = $s3object['key'];
68
+
69
+ $path_parts = pathinfo( $main_file );
70
+ $prefix = trailingslashit( dirname( $s3object['key'] ) );
71
+
72
+ // Used to search S3 for all files related to an attachment
73
+ $search_prefix = $prefix . basename( $main_file, '.' . $path_parts['extension'] );
74
+
75
+ $args = array(
76
+ 'Bucket' => $s3object['bucket'],
77
+ 'Prefix' => $search_prefix,
78
+ );
79
+
80
+ try {
81
+ // List objects for the attachment
82
+ $result = $s3client->ListObjects( $args );
83
+ } catch ( Exception $e ) {
84
+ error_log( 'Error listing objects of prefix ' . $search_prefix . ' for attachment ' . $attachment->ID . ' from S3: ' . $e->getMessage() );
85
+ $this->error_count ++;
86
+
87
+ return false;
88
+ }
89
+
90
+ $file_size_total = 0;
91
+ $main_file_size = 0;
92
+
93
+ foreach ( $result->get( 'Contents' ) as $object ) {
94
+ if ( ! isset( $object['Size'] ) ) {
95
+ continue;
96
+ }
97
+
98
+ $size = $object['Size'];
99
+
100
+ // Increment the total size of files for the attachment
101
+ $file_size_total += $size;
102
+
103
+ if ( $object['Key'] === $main_file ) {
104
+ // Record the size of the main file
105
+ $main_file_size = $size;
106
+ }
107
+ }
108
+
109
+ if ( 0 === $file_size_total ) {
110
+ error_log( 'Total file size for the attachment is 0: ' . $attachment->ID );
111
+ $this->error_count ++;
112
+
113
+ return false;
114
+ }
115
+
116
+ // Update the main file size for the attachment
117
+ $meta = get_post_meta( $attachment->ID, '_wp_attachment_metadata', true );
118
+ $meta['filesize'] = $main_file_size;
119
+ update_post_meta( $attachment->ID, '_wp_attachment_metadata', $meta );
120
+
121
+ // Add the total file size for all image sizes
122
+ update_post_meta( $attachment->ID, 'wpos3_filesize_total', $file_size_total );
123
+
124
+ return true;
125
+ }
126
+
127
+ /**
128
+ * Get a count of all attachments without region in their S3 metadata
129
+ * for the whole site
130
+ *
131
+ * @return int
132
+ */
133
+ function count_attachments_to_process() {
134
+ // get the table prefixes for all the blogs
135
+ $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
136
+ $all_count = 0;
137
+
138
+ foreach ( $table_prefixes as $blog_id => $table_prefix ) {
139
+ $count = $this->get_attachments_removed_from_server( $table_prefix, true );
140
+ $all_count += $count;
141
+ }
142
+
143
+ return $all_count;
144
+ }
145
+
146
+ /**
147
+ * Get all attachments that don't have region in their S3 meta data for a blog
148
+ *
149
+ * @param string $prefix
150
+ * @param int $limit
151
+ *
152
+ * @return mixed
153
+ */
154
+ function get_attachments_to_process( $prefix, $limit ) {
155
+ $attachments = $this->get_attachments_removed_from_server( $prefix, false, $limit );
156
+
157
+ return $attachments;
158
+ }
159
+
160
+ /**
161
+ * Wrapper for database call to get attachments uploaded to S3,
162
+ * that don't have the file size meta added already
163
+ *
164
+ * @param string $prefix
165
+ * @param null|int $limit
166
+ *
167
+ * @return mixed
168
+ */
169
+ function get_s3_attachments( $prefix, $limit = null ) {
170
+ global $wpdb;
171
+
172
+ $sql = "SELECT pm1.`post_id` as `ID`, pm1.`meta_value` AS 's3object'
173
+ FROM `{$prefix}postmeta` pm1
174
+ LEFT OUTER JOIN `{$prefix}postmeta` pm2
175
+ ON pm1.`post_id` = pm2.`post_id`
176
+ AND pm2.`meta_key` = 'wpos3_filesize_total'
177
+ WHERE pm1.`meta_key` = 'amazonS3_info'
178
+ AND pm2.`post_id` is null";
179
+
180
+ if ( ! is_null( $limit ) ) {
181
+ $sql .= ' LIMIT %d';
182
+
183
+ $sql = $wpdb->prepare( $sql, $limit );
184
+ }
185
+
186
+ return $wpdb->get_results( $sql, OBJECT );
187
+ }
188
+
189
+ /**
190
+ * Get S3 attachments that have had their local file removed from the server
191
+ *
192
+ * @param string $prefix
193
+ * @param bool|false $count
194
+ * @param null|int $limit
195
+ *
196
+ * @return array|int
197
+ */
198
+ function get_attachments_removed_from_server( $prefix, $count = false, $limit = null ) {
199
+ $all_attachments = $this->get_s3_attachments( $prefix, $limit );
200
+ $attachments = array();
201
+
202
+ foreach ( $all_attachments as $attachment ) {
203
+ if ( ! file_exists( get_attached_file( $attachment->ID, true ) ) ) {
204
+ $attachments[] = $attachment;
205
+ }
206
+ }
207
+
208
+ if ( $count ) {
209
+ return count( $attachments );
210
+ }
211
+
212
+ return $attachments;
213
+ }
214
+
215
+ }
classes/upgrades/as3cf-region-meta.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Upgrade Region in Meta
4
+ *
5
+ * @package amazon-s3-and-cloudfront
6
+ * @subpackage Classes/Upgrades/Region-Meta
7
+ * @copyright Copyright (c) 2014, Delicious Brains
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.6.2
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * AS3CF_Upgrade_Region_Meta Class
19
+ *
20
+ * This class handles updating the region of the attachment's bucket in the meta data
21
+ *
22
+ * @since 0.6.2
23
+ */
24
+ class AS3CF_Upgrade_Region_Meta extends AS3CF_Upgrade {
25
+
26
+ /**
27
+ * Initiate the upgrade
28
+ *
29
+ * @param object $as3cf Instance of calling class
30
+ */
31
+ public function __construct( $as3cf ) {
32
+ $this->upgrade_id = 1;
33
+ $this->upgrade_name = 'meta_with_region';
34
+ $this->upgrade_type = 'metadata';
35
+
36
+ $this->running_update_text = __( 'and updating the metadata with the bucket region it is served from. This will allow us to serve your files from the proper S3 region subdomain <span style="white-space:nowrap;">(e.g. s3-us-west-2.amazonaws.com)</span>.', 'as3cf' );
37
+
38
+ parent::__construct( $as3cf );
39
+ }
40
+
41
+ /**
42
+ * Get the region for the bucket where an attachment is located, update the S3 meta.
43
+ *
44
+ * @param $attachment
45
+ *
46
+ * @return bool
47
+ */
48
+ function upgrade_attachment( $attachment ) {
49
+ $s3object = unserialize( $attachment->s3object );
50
+ if ( false === $s3object ) {
51
+ error_log( 'Failed to unserialize S3 meta for attachment ' . $attachment->ID . ': ' . $attachment->s3object );
52
+ $this->error_count++;
53
+
54
+ return false;
55
+ }
56
+ // retrieve region and update the attachment metadata
57
+ $region = $this->as3cf->get_s3object_region( $s3object, $attachment->ID );
58
+ if ( is_wp_error( $region ) ) {
59
+ error_log( 'Error updating region: ' . $region->get_error_message() );
60
+ $this->error_count++;
61
+
62
+ return false;
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Get a count of all attachments without region in their S3 metadata
70
+ * for the whole site
71
+ *
72
+ * @return int
73
+ */
74
+ function count_attachments_to_process() {
75
+ // get the table prefixes for all the blogs
76
+ $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
77
+ $all_count = 0;
78
+
79
+ foreach ( $table_prefixes as $blog_id => $table_prefix ) {
80
+ $count = $this->count_attachments_without_region( $table_prefix );
81
+ $all_count += $count;
82
+ }
83
+
84
+ return $all_count;
85
+ }
86
+
87
+ /**
88
+ * Get all attachments that don't have region in their S3 meta data for a blog
89
+ *
90
+ * @param string $prefix
91
+ * @param int $limit
92
+ *
93
+ * @return mixed
94
+ */
95
+ function get_attachments_to_process( $prefix, $limit ) {
96
+ $attachments = $this->get_attachments_without_region_results( $prefix, false, $limit );
97
+
98
+ return $attachments;
99
+ }
100
+
101
+ /**
102
+ * Get a count of attachments that don't have region in their S3 meta data for a blog
103
+ * @param $prefix
104
+ *
105
+ * @return int
106
+ */
107
+ function count_attachments_without_region( $prefix ) {
108
+ $count = $this->get_attachments_without_region_results( $prefix, true );
109
+
110
+ return $count;
111
+ }
112
+
113
+ /**
114
+ * Wrapper for database call to get attachments without region
115
+ *
116
+ * @param string $prefix
117
+ * @param bool $count return count of attachments
118
+ * @param null|int $limit
119
+ *
120
+ * @return mixed
121
+ */
122
+ function get_attachments_without_region_results( $prefix, $count = false, $limit = null ) {
123
+ global $wpdb;
124
+
125
+ $sql = " FROM `{$prefix}postmeta`
126
+ WHERE `meta_key` = 'amazonS3_info'
127
+ AND `meta_value` NOT LIKE '%%\"region\"%%'";
128
+
129
+ if ( $count ) {
130
+ $sql = 'SELECT COUNT(*)' . $sql;
131
+
132
+ return $wpdb->get_var( $sql );
133
+ }
134
+
135
+ $sql = "SELECT `post_id` as `ID`, `meta_value` AS 's3object'" . $sql;
136
+
137
+ if ( ! is_null( $limit ) ) {
138
+ $sql .= ' LIMIT %d';
139
+
140
+ $sql = $wpdb->prepare( $sql, $limit );
141
+ }
142
+
143
+ return $wpdb->get_results( $sql, OBJECT );
144
+ }
145
+ }
classes/wp-aws-compatibility-check.php CHANGED
@@ -39,13 +39,18 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
39
  */
40
  protected $plugin_file_path;
41
 
 
 
 
 
 
42
  /**
43
  * @var null|string The key of the required parent plugin, e.g. amazon-web-services
44
  */
45
  protected $parent_plugin_slug;
46
 
47
  /**
48
- * @var null|int The required version of the parent plugin
49
  */
50
  protected $parent_plugin_required_version;
51
 
@@ -74,6 +79,22 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
74
  */
75
  protected $notice_class = 'error';
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  function __construct( $plugin_name, $plugin_slug, $plugin_file_path, $parent_plugin_name = null, $parent_plugin_slug = null, $parent_plugin_required_version = null, $parent_plugin_filename = null, $deactivate_if_not_compatible = false, $parent_plugin_url = null ) {
78
  $this->plugin_name = $plugin_name;
79
  $this->plugin_slug = $plugin_slug;
@@ -117,7 +138,7 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
117
  * @return string
118
  */
119
  function get_parent_plugin_name() {
120
- if ( $this->parent_plugin_name ) {
121
  return $this->parent_plugin_name;
122
  }
123
 
@@ -127,10 +148,10 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
127
  /**
128
  * Get the class of the parent plugin
129
  *
130
- * @return mixed|string
131
  */
132
  function get_parent_plugin_class() {
133
- if ( $this->parent_plugin_slug ) {
134
  $class = ucwords( str_replace( '-', ' ', $this->parent_plugin_slug ) );
135
 
136
  return str_replace( ' ', '_', $class );
@@ -175,7 +196,7 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
175
  /**
176
  * Get the URL for the parent plugin. Defaults to a wordpress.org URL.
177
  *
178
- * @return null|string
179
  */
180
  function get_parent_plugin_url() {
181
  if ( ! is_null( $this->parent_plugin_slug ) ) {
@@ -399,10 +420,8 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
399
  return;
400
  }
401
 
402
- global $pagenow;
403
-
404
- if ( 'update.php' === $pagenow && isset( $_GET['action'] ) && 'install-plugin' === $_GET['action'] ) {
405
- // Don't show notice when installing plugins
406
  return;
407
  }
408
 
@@ -415,7 +434,7 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
415
  function get_admin_notice() {
416
  $error_msg = $this->get_error_msg();
417
 
418
- if ( ! $error_msg ) {
419
  return;
420
  }
421
 
@@ -447,24 +466,37 @@ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
447
  * @return bool
448
  */
449
  public static function is_installing_or_updating_plugins() {
 
 
 
 
 
 
450
  global $pagenow;
451
 
452
  if ( 'update.php' === $pagenow && isset( $_GET['action'] ) && 'install-plugin' === $_GET['action'] ) {
453
  // We are installing a plugin
454
- return true;
455
  }
456
 
457
- if ( 'plugins.php' === $pagenow && isset( $_POST['action'] ) && 'update-selected' === $_POST['action'] ) {
458
- // We are updating plugins from the plugin page
459
- return true;
 
 
 
 
 
 
 
460
  }
461
 
462
  if ( 'update-core.php' === $pagenow && isset( $_GET['action'] ) && 'do-plugin-upgrade' === $_GET['action'] ) {
463
  // We are updating plugins from the updates page
464
- return true;
465
  }
466
 
467
- return false;
468
  }
469
  }
470
  }
39
  */
40
  protected $plugin_file_path;
41
 
42
+ /**
43
+ * @var null|string The name of the required parent plugin
44
+ */
45
+ protected $parent_plugin_name;
46
+
47
  /**
48
  * @var null|string The key of the required parent plugin, e.g. amazon-web-services
49
  */
50
  protected $parent_plugin_slug;
51
 
52
  /**
53
+ * @var null|string The required version of the parent plugin
54
  */
55
  protected $parent_plugin_required_version;
56
 
79
  */
80
  protected $notice_class = 'error';
81
 
82
+ /**
83
+ * @var bool Used to store if we are installing or updating plugins once per page request
84
+ */
85
+ protected static $is_installing_or_updating_plugins;
86
+
87
+ /**
88
+ * @param string $plugin_name
89
+ * @param string $plugin_slug
90
+ * @param string $plugin_file_path
91
+ * @param string|null $parent_plugin_name
92
+ * @param string|null $parent_plugin_slug
93
+ * @param string|null $parent_plugin_required_version
94
+ * @param string|null $parent_plugin_filename
95
+ * @param bool|false $deactivate_if_not_compatible
96
+ * @param string|null $parent_plugin_url
97
+ */
98
  function __construct( $plugin_name, $plugin_slug, $plugin_file_path, $parent_plugin_name = null, $parent_plugin_slug = null, $parent_plugin_required_version = null, $parent_plugin_filename = null, $deactivate_if_not_compatible = false, $parent_plugin_url = null ) {
99
  $this->plugin_name = $plugin_name;
100
  $this->plugin_slug = $plugin_slug;
138
  * @return string
139
  */
140
  function get_parent_plugin_name() {
141
+ if ( ! is_null( $this->parent_plugin_name ) ) {
142
  return $this->parent_plugin_name;
143
  }
144
 
148
  /**
149
  * Get the class of the parent plugin
150
  *
151
+ * @return string
152
  */
153
  function get_parent_plugin_class() {
154
+ if ( ! is_null( $this->parent_plugin_slug ) ) {
155
  $class = ucwords( str_replace( '-', ' ', $this->parent_plugin_slug ) );
156
 
157
  return str_replace( ' ', '_', $class );
196
  /**
197
  * Get the URL for the parent plugin. Defaults to a wordpress.org URL.
198
  *
199
+ * @return string
200
  */
201
  function get_parent_plugin_url() {
202
  if ( ! is_null( $this->parent_plugin_slug ) ) {
420
  return;
421
  }
422
 
423
+ if ( self::is_installing_or_updating_plugins() ) {
424
+ // Don't show notice when installing or updating plugins
 
 
425
  return;
426
  }
427
 
434
  function get_admin_notice() {
435
  $error_msg = $this->get_error_msg();
436
 
437
+ if ( false === $error_msg || '' === $error_msg ) {
438
  return;
439
  }
440
 
466
  * @return bool
467
  */
468
  public static function is_installing_or_updating_plugins() {
469
+ if ( ! is_null( self::$is_installing_or_updating_plugins ) ) {
470
+ return self::$is_installing_or_updating_plugins;
471
+ }
472
+
473
+ self::$is_installing_or_updating_plugins = false;
474
+
475
  global $pagenow;
476
 
477
  if ( 'update.php' === $pagenow && isset( $_GET['action'] ) && 'install-plugin' === $_GET['action'] ) {
478
  // We are installing a plugin
479
+ self::$is_installing_or_updating_plugins = true;
480
  }
481
 
482
+ if ( 'plugins.php' === $pagenow && isset( $_POST['action'] ) ) {
483
+ $action = $_POST['action'];
484
+ if ( isset( $_POST['action2'] ) && '-1' !== $_POST['action2'] ) {
485
+ $action = $_POST['action2'];
486
+ }
487
+
488
+ if ( 'update-selected' === $action ) {
489
+ // We are updating plugins from the plugin page
490
+ self::$is_installing_or_updating_plugins = true;
491
+ }
492
  }
493
 
494
  if ( 'update-core.php' === $pagenow && isset( $_GET['action'] ) && 'do-plugin-upgrade' === $_GET['action'] ) {
495
  // We are updating plugins from the updates page
496
+ self::$is_installing_or_updating_plugins = true;
497
  }
498
 
499
+ return self::$is_installing_or_updating_plugins;
500
  }
501
  }
502
  }
classes/wp-aws-uninstall.php CHANGED
@@ -26,37 +26,45 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
26
  class WP_AWS_Uninstall {
27
 
28
  /**
29
- * @var array Options to be deleted
30
  */
31
  protected $options;
32
 
33
  /**
34
- * @var array Post meta to be deleted
35
  */
36
  protected $postmeta;
37
 
38
  /**
39
- * @var array Cron hooks to be unscheduled
40
  */
41
  protected $crons;
42
 
43
  /**
44
- * @var array Transients to be deleted
 
 
 
 
 
 
 
 
45
  */
46
  protected $transients;
47
 
48
  /**
49
- * @var Blog(s) in site
50
  */
51
  protected $blog_ids;
52
 
53
  /**
54
  * WP_AWS_Uninstall constructor.
55
  *
56
- * @param array $options
57
- * @param array $postmeta
58
- * @param array $crons
59
- * @param array $transients
60
  */
61
  public function __construct(
62
  $options = array(),
@@ -64,10 +72,10 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
64
  $crons = array(),
65
  $transients = array()
66
  ) {
67
- $this->options = $options;
68
- $this->postmeta = $postmeta;
69
- $this->crons = $crons;
70
- $this->transients = $transients;
71
 
72
  $this->set_blog_ids();
73
 
@@ -81,7 +89,7 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
81
  * Set the blog id(s) for a site
82
  */
83
  private function set_blog_ids() {
84
- $blog_ids[] = 1;
85
  if ( function_exists( 'is_multisite' ) && is_multisite() ) {
86
  $args = array(
87
  'limit' => false,
@@ -97,33 +105,42 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
97
  }
98
 
99
  /**
100
- * Check and ensure a property has been filled with an array
101
  *
102
- * @param string $property
103
  *
104
  * @return bool
105
  */
106
- private function check_property( $property ) {
107
- if ( empty( $this->$property ) ) {
108
- return false;
 
 
109
  }
110
 
111
- if ( ! is_array( $this->$property ) ) {
112
- // Convert any strings to an array
113
- $this->$property = array( $this->$property );
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
 
116
- return true;
117
  }
118
 
119
  /**
120
  * Delete site wide options
121
  */
122
  public function delete_options() {
123
- if ( ! $this->check_property( 'options' ) ) {
124
- return;
125
- }
126
-
127
  foreach ( $this->options as $option ) {
128
  delete_site_option( $option );
129
  }
@@ -133,10 +150,6 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
133
  * Delete post meta data for all blogs
134
  */
135
  public function delete_postmeta() {
136
- if ( ! $this->check_property( 'postmeta' ) ) {
137
- return;
138
- }
139
-
140
  global $wpdb;
141
 
142
  foreach ( $this->blog_ids as $blog_id ) {
@@ -153,10 +166,6 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
153
  * Clear any scheduled cron jobs
154
  */
155
  public function clear_crons() {
156
- if ( ! $this->check_property( 'crons' ) ) {
157
- return;
158
- }
159
-
160
  foreach ( $this->crons as $cron ) {
161
  $timestamp = wp_next_scheduled( $cron );
162
  if ( $timestamp ) {
@@ -166,15 +175,44 @@ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
166
  }
167
 
168
  /**
169
- * Delete site wide transients
170
  */
171
  public function delete_transients() {
172
- if ( ! $this->check_property( 'transients' ) ) {
 
 
 
 
 
173
  return;
174
  }
175
 
176
- foreach ( $this->transients as $transient ) {
177
- delete_site_transient( $transient );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
  }
180
  }
26
  class WP_AWS_Uninstall {
27
 
28
  /**
29
+ * @var array|string Options to be deleted
30
  */
31
  protected $options;
32
 
33
  /**
34
+ * @var array|string Post meta to be deleted
35
  */
36
  protected $postmeta;
37
 
38
  /**
39
+ * @var array|string Cron hooks to be unscheduled
40
  */
41
  protected $crons;
42
 
43
  /**
44
+ * @var array|string Transients to be deleted, this can be site wide and subsite, e.g.
45
+ *
46
+ * array(
47
+ * 'site' => array(...),
48
+ * 'subsite' => array(...),
49
+ * )
50
+ *
51
+ * By default, an array of transients will be treated as site wide.
52
+ *
53
  */
54
  protected $transients;
55
 
56
  /**
57
+ * @var array Blog(s) in site
58
  */
59
  protected $blog_ids;
60
 
61
  /**
62
  * WP_AWS_Uninstall constructor.
63
  *
64
+ * @param array|string $options
65
+ * @param array|string $postmeta
66
+ * @param array|string $crons
67
+ * @param array|string $transients
68
  */
69
  public function __construct(
70
  $options = array(),
72
  $crons = array(),
73
  $transients = array()
74
  ) {
75
+ $this->options = $this->maybe_convert_to_array( $options );
76
+ $this->postmeta = $this->maybe_convert_to_array( $postmeta );
77
+ $this->crons = $this->maybe_convert_to_array( $crons );
78
+ $this->transients = $this->maybe_convert_to_array( $transients );
79
 
80
  $this->set_blog_ids();
81
 
89
  * Set the blog id(s) for a site
90
  */
91
  private function set_blog_ids() {
92
+ $blog_ids = array( 1 );
93
  if ( function_exists( 'is_multisite' ) && is_multisite() ) {
94
  $args = array(
95
  'limit' => false,
105
  }
106
 
107
  /**
108
+ * Is the current blog ID that specified in wp-config.php
109
  *
110
+ * @param int $blog_id
111
  *
112
  * @return bool
113
  */
114
+ private function is_current_blog( $blog_id ) {
115
+ $default = defined( 'BLOG_ID_CURRENT_SITE' ) ? BLOG_ID_CURRENT_SITE : 1;
116
+
117
+ if ( $default === $blog_id ) {
118
+ return true;
119
  }
120
 
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Helper to ensure a value is an array
126
+ *
127
+ * @param array|string $data
128
+ *
129
+ * @return array
130
+ */
131
+ private function maybe_convert_to_array( $data ) {
132
+ if ( ! is_array( $data ) ) {
133
+ // Convert a string to an array
134
+ $data = array( $data );
135
  }
136
 
137
+ return $data;
138
  }
139
 
140
  /**
141
  * Delete site wide options
142
  */
143
  public function delete_options() {
 
 
 
 
144
  foreach ( $this->options as $option ) {
145
  delete_site_option( $option );
146
  }
150
  * Delete post meta data for all blogs
151
  */
152
  public function delete_postmeta() {
 
 
 
 
153
  global $wpdb;
154
 
155
  foreach ( $this->blog_ids as $blog_id ) {
166
  * Clear any scheduled cron jobs
167
  */
168
  public function clear_crons() {
 
 
 
 
169
  foreach ( $this->crons as $cron ) {
170
  $timestamp = wp_next_scheduled( $cron );
171
  if ( $timestamp ) {
175
  }
176
 
177
  /**
178
+ * Delete transients
179
  */
180
  public function delete_transients() {
181
+ if ( ! isset( $this->transients['site'] ) && ! isset( $this->transients['subsite'] ) ) {
182
+ // Single array of site wide transients
183
+ foreach ( $this->transients as $transient ) {
184
+ delete_site_transient( $transient );
185
+ }
186
+
187
  return;
188
  }
189
 
190
+ // Deal with site wide transients
191
+ if ( isset( $this->transients['site'] ) ) {
192
+ $site_transients = $this->maybe_convert_to_array( $this->transients['site'] );
193
+
194
+ foreach ( $site_transients as $transient ) {
195
+ delete_site_transient( $transient );
196
+ }
197
+ }
198
+
199
+ // Deal with subsite specific transients
200
+ if ( isset( $this->transients['subsite'] ) ) {
201
+ $subsite_transients = $this->maybe_convert_to_array( $this->transients['subsite'] );
202
+
203
+ foreach ( $this->blog_ids as $blog_id ) {
204
+ if ( is_multisite() && ! $this->is_current_blog( $blog_id ) ) {
205
+ switch_to_blog( $blog_id );
206
+ }
207
+
208
+ foreach ( $subsite_transients as $transient ) {
209
+ delete_transient( $transient );
210
+ }
211
+ }
212
+
213
+ if ( is_multisite() && ! $this->is_current_blog( $blog_id ) ) {
214
+ restore_current_blog();
215
+ }
216
  }
217
  }
218
  }
composer.lock DELETED
@@ -1,107 +0,0 @@
1
- {
2
- "_readme": [
3
- "This file locks the dependencies of your project to a known state",
4
- "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
- "This file is @generated automatically"
6
- ],
7
- "hash": "4b6bce2aaae303cfd3fc478e7cc55366",
8
- "packages": [
9
- {
10
- "name": "composer/installers",
11
- "version": "v1.0.18",
12
- "source": {
13
- "type": "git",
14
- "url": "https://github.com/composer/installers.git",
15
- "reference": "74fb0a7a1a23696d9c8cc2fba5903f6711cdd067"
16
- },
17
- "dist": {
18
- "type": "zip",
19
- "url": "https://api.github.com/repos/composer/installers/zipball/74fb0a7a1a23696d9c8cc2fba5903f6711cdd067",
20
- "reference": "74fb0a7a1a23696d9c8cc2fba5903f6711cdd067",
21
- "shasum": ""
22
- },
23
- "replace": {
24
- "roundcube/plugin-installer": "*",
25
- "shama/baton": "*"
26
- },
27
- "require-dev": {
28
- "composer/composer": "1.0.*@dev",
29
- "phpunit/phpunit": "4.1.*"
30
- },
31
- "type": "composer-installer",
32
- "extra": {
33
- "class": "Composer\\Installers\\Installer",
34
- "branch-alias": {
35
- "dev-master": "1.0-dev"
36
- }
37
- },
38
- "autoload": {
39
- "psr-0": {
40
- "Composer\\Installers\\": "src/"
41
- }
42
- },
43
- "notification-url": "https://packagist.org/downloads/",
44
- "license": [
45
- "MIT"
46
- ],
47
- "authors": [
48
- {
49
- "name": "Kyle Robinson Young",
50
- "email": "kyle@dontkry.com",
51
- "homepage": "https://github.com/shama"
52
- }
53
- ],
54
- "description": "A multi-framework Composer library installer",
55
- "homepage": "http://composer.github.com/installers/",
56
- "keywords": [
57
- "Craft",
58
- "Dolibarr",
59
- "Hurad",
60
- "MODX Evo",
61
- "OXID",
62
- "WolfCMS",
63
- "agl",
64
- "annotatecms",
65
- "bitrix",
66
- "cakephp",
67
- "chef",
68
- "codeigniter",
69
- "concrete5",
70
- "croogo",
71
- "drupal",
72
- "elgg",
73
- "fuelphp",
74
- "installer",
75
- "joomla",
76
- "kohana",
77
- "laravel",
78
- "lithium",
79
- "magento",
80
- "mako",
81
- "mediawiki",
82
- "modulework",
83
- "moodle",
84
- "phpbb",
85
- "piwik",
86
- "ppi",
87
- "puppet",
88
- "roundcube",
89
- "shopware",
90
- "silverstripe",
91
- "symfony",
92
- "typo3",
93
- "wordpress",
94
- "zend",
95
- "zikula"
96
- ],
97
- "time": "2014-08-18 20:00:12"
98
- }
99
- ],
100
- "packages-dev": [],
101
- "aliases": [],
102
- "minimum-stability": "stable",
103
- "stability-flags": [],
104
- "prefer-stable": false,
105
- "platform": [],
106
- "platform-dev": []
107
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/amazon-s3-and-cloudfront-en.pot CHANGED
@@ -8,7 +8,7 @@ msgid ""
8
  msgstr ""
9
  "Project-Id-Version: amazon-s3-and-cloudfront\n"
10
  "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n"
11
- "POT-Creation-Date: 2015-08-17 15:41-0300\n"
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,116 +17,120 @@ msgstr ""
17
  "Content-Type: text/plain; charset=UTF-8\n"
18
  "Content-Transfer-Encoding: 8bit\n"
19
 
20
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:84
21
  msgid "Offload S3"
22
  msgstr ""
23
 
24
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:85
25
  msgid "S3 and CloudFront"
26
  msgstr ""
27
 
28
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:519
29
  #, php-format
30
  msgid "File %s does not exist"
31
  msgstr ""
32
 
33
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:528
34
  #, php-format
35
  msgid "Mime type %s is not allowed"
36
  msgstr ""
37
 
38
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:605
39
  #, php-format
40
  msgid "Error uploading %s to S3: %s"
41
  msgstr ""
42
 
43
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1173
44
  msgid "Cheatin&#8217; eh?"
45
  msgstr ""
46
 
47
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1177
48
  msgid "You do not have sufficient permissions to access this page."
49
  msgstr ""
50
 
51
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1183
52
  msgid "No bucket name provided."
53
  msgstr ""
54
 
55
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1455
 
 
 
 
56
  #, php-format
57
  msgid "There was an error attempting to get the region of the bucket %s: %s"
58
  msgstr ""
59
 
60
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1575
61
  msgid ""
62
  "This is a test file to check if the user has write permission to S3. Delete "
63
  "me if found."
64
  msgstr ""
65
 
66
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1607
67
  #, php-format
68
  msgid ""
69
  "There was an error attempting to check the permissions of the bucket %s: %s"
70
  msgstr ""
71
 
72
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1665
73
  msgid "Error creating bucket"
74
  msgstr ""
75
 
76
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1666
77
  msgid "Bucket name too short."
78
  msgstr ""
79
 
80
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1667
81
  msgid "Bucket name too long."
82
  msgstr ""
83
 
84
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1668
85
  msgid ""
86
  "Invalid character. Bucket names can contain lowercase letters, numbers, "
87
  "periods and hyphens."
88
  msgstr ""
89
 
90
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1669
91
  msgid "Error saving bucket"
92
  msgstr ""
93
 
94
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1670
95
  msgid "Error fetching buckets"
96
  msgstr ""
97
 
98
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1671
99
  msgid "Error getting URL preview: "
100
  msgstr ""
101
 
102
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1672
103
  msgid "The changes you made will be lost if you navigate away from this page"
104
  msgstr ""
105
 
106
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1730
107
  msgid "Cheatin' eh?"
108
  msgstr ""
109
 
110
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1833
111
  msgctxt "Show the media library tab"
112
  msgid "Media Library"
113
  msgstr ""
114
 
115
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1834
116
  msgctxt "Show the support tab"
117
  msgid "Support"
118
  msgstr ""
119
 
120
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1970
121
  #, php-format
122
  msgid "The file %s has been given %s permissions on Amazon S3."
123
  msgstr ""
124
 
125
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:2430
126
  msgid "Quick Start Guide"
127
  msgstr ""
128
 
129
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:2432
130
  #, php-format
131
  msgid ""
132
  "Looks like we don't have write access to this bucket. It's likely that the "
@@ -135,7 +139,7 @@ msgid ""
135
  "correctly."
136
  msgstr ""
137
 
138
- #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:2434
139
  #, php-format
140
  msgid ""
141
  "Looks like we don't have access to the buckets. It's likely that the user "
@@ -143,370 +147,392 @@ msgid ""
143
  "Please see our %s for instructions on setting up permissions correctly."
144
  msgstr ""
145
 
146
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-plugin-compatibility.php:260
147
  #, php-format
148
  msgid "There was an error attempting to download the file %s from S3: %s"
149
  msgstr ""
150
 
151
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:104
152
  #, php-format
153
  msgid ""
154
- "<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through "
155
- "all the Media Library items uploaded to S3 and updating the metadata with "
156
- "the bucket region it is served from. This will allow us to serve your files "
157
- "from the proper S3 region subdomain <span style=\"white-space:nowrap;\">(e."
158
- "g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the "
159
  "background, processing a small batch of Media Library items every %d "
160
  "minutes. There should be no noticeable impact on your server&#8217;s "
161
  "performance."
162
  msgstr ""
163
 
164
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:105
165
  msgid "Pause Update"
166
  msgstr ""
167
 
168
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:109
 
169
  msgid ""
170
- "<strong>Metadata Update Paused</strong> &mdash; Updating Media Library "
171
- "metadata has been paused."
172
  msgstr ""
173
 
174
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:110
175
  msgid "Restart Update"
176
  msgstr ""
177
 
178
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:113
 
179
  msgid ""
180
- "<strong>Error Updating Metadata</strong> &mdash; We ran into some errors "
181
- "attempting to update the metadata for all your Media Library items that have "
182
- "been uploaded to S3. Please check your error log for details."
183
  msgstr ""
184
 
185
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:114
186
  msgid "Try Run It Again"
187
  msgstr ""
188
 
189
- #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:183
190
  #, php-format
191
  msgid "Every %d Minutes"
192
  msgstr ""
193
 
194
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  msgid "deactivate"
196
  msgstr ""
197
 
198
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:290
199
  #, php-format
200
  msgid "You can %s the %s plugin to get rid of this notice."
201
  msgstr ""
202
 
203
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:293
204
  #, php-format
205
  msgid "%s has been disabled as it requires the %s plugin."
206
  msgstr ""
207
 
208
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:297
209
  msgid "which is currently disabled."
210
  msgstr ""
211
 
212
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:299
213
  msgid "It appears to be installed already."
214
  msgstr ""
215
 
216
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:301
217
  msgctxt "Activate plugin"
218
  msgid "Activate it now."
219
  msgstr ""
220
 
221
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:308
222
  #, php-format
223
  msgid "<a href=\"%s\">Install</a> and activate it."
224
  msgstr ""
225
 
226
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:319
227
  #, php-format
228
  msgid ""
229
  "%s has been disabled as it requires version %s or later of the %s plugin."
230
  msgstr ""
231
 
232
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:322
233
  #, php-format
234
  msgid "You currently have version %s installed."
235
  msgstr ""
236
 
237
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:329
238
  #, php-format
239
  msgid "A valid license for %s is required to update."
240
  msgstr ""
241
 
242
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:337
243
  msgid "Update to the latest version"
244
  msgstr ""
245
 
246
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:349
247
  #, php-format
248
  msgid ""
249
  "%1$s has been disabled because it is not a supported addon of the %2$s "
250
  "plugin."
251
  msgstr ""
252
 
253
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:358
254
  #, php-format
255
  msgid ""
256
  "%1$s has been disabled because it will not work with the version of the %2$s "
257
  "plugin installed. %1$s %3$s or later is required."
258
  msgstr ""
259
 
260
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:361
261
  #, php-format
262
  msgid "Update %s to the latest version"
263
  msgstr ""
264
 
265
- #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:423
266
  #, php-format
267
  msgid "The %s plugin has been deactivated."
268
  msgstr ""
269
 
270
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:3
271
  msgid "Change bucket"
272
  msgstr ""
273
 
274
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:3
275
  msgid "What bucket would you like to use?"
276
  msgstr ""
277
 
278
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:5
279
  msgid "Existing bucket name"
280
  msgstr ""
281
 
282
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:7
283
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:12
284
  msgid "Saving..."
285
  msgstr ""
286
 
287
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:7
288
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:12
289
  msgid "Save Bucket"
290
  msgstr ""
291
 
292
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:8
293
  msgid "Browse existing buckets"
294
  msgstr ""
295
 
296
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:9
297
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:26
298
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:31
299
  msgid "Create new bucket"
300
  msgstr ""
301
 
302
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:13
303
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:21
304
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:61
305
  msgid "Cancel"
306
  msgstr ""
307
 
308
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:18
309
  msgid "Select bucket"
310
  msgstr ""
311
 
312
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:19
313
  msgid "Loading..."
314
  msgstr ""
315
 
316
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:22
317
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:27
318
  msgid "Refresh"
319
  msgstr ""
320
 
321
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:25
322
  msgid "Enter bucket name"
323
  msgstr ""
324
 
325
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:37
326
  msgid "Bucket Name:"
327
  msgstr ""
328
 
329
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:40
330
  msgid "Bucket Name"
331
  msgstr ""
332
 
333
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:47
334
  msgid "Region:"
335
  msgstr ""
336
 
337
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:60
 
 
 
 
 
338
  msgid "Creating..."
339
  msgstr ""
340
 
341
- #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:60
342
  msgid "Create New Bucket"
343
  msgstr ""
344
 
345
- #: builds/amazon-s3-and-cloudfront/view/bucket-setting.php:7
346
  msgid "Bucket"
347
  msgstr ""
348
 
349
- #: builds/amazon-s3-and-cloudfront/view/bucket-setting.php:11
350
  msgid "Change"
351
  msgstr ""
352
 
353
- #: builds/amazon-s3-and-cloudfront/view/debug-info.php:2
 
 
 
 
354
  msgid "Diagnostic Info"
355
  msgstr ""
356
 
357
- #: builds/amazon-s3-and-cloudfront/view/debug-info.php:11
358
  msgctxt "Download to your computer"
359
  msgid "Download"
360
  msgstr ""
361
 
362
- #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:5
363
  msgid "Domain:"
364
  msgstr ""
365
 
366
- #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:23
367
  msgid "Bucket name as subdomain"
368
  msgstr ""
369
 
370
- #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:28
371
  msgid "Bucket name in path"
372
  msgstr ""
373
 
374
- #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:33
375
  msgid "Bucket name as domain"
376
  msgstr ""
377
 
378
- #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:38
379
  msgid "CloudFront or custom domain"
380
  msgstr ""
381
 
382
- #: builds/amazon-s3-and-cloudfront/view/error-access.php:4
383
  msgid "Access Denied to Bucket"
384
  msgstr ""
385
 
386
- #: builds/amazon-s3-and-cloudfront/view/settings.php:10
387
  msgid "Settings saved."
388
  msgstr ""
389
 
390
- #: builds/amazon-s3-and-cloudfront/view/settings.php:44
391
  msgid "Enable/Disable the Plugin"
392
  msgstr ""
393
 
394
- #: builds/amazon-s3-and-cloudfront/view/settings.php:51
395
  msgid "Copy Files to S3"
396
  msgstr ""
397
 
398
- #: builds/amazon-s3-and-cloudfront/view/settings.php:52
399
  msgid ""
400
  "When a file is uploaded to the Media Library, copy it to S3. Existing files "
401
  "are <em>not</em> copied to S3."
402
  msgstr ""
403
 
404
- #: builds/amazon-s3-and-cloudfront/view/settings.php:60
405
  msgid "Rewrite File URLs"
406
  msgstr ""
407
 
408
- #: builds/amazon-s3-and-cloudfront/view/settings.php:61
409
  msgid ""
410
  "For Media Library files that have been copied to S3, rewrite the URLs so "
411
  "that they are served from S3/CloudFront instead of your server."
412
  msgstr ""
413
 
414
- #: builds/amazon-s3-and-cloudfront/view/settings.php:65
415
  msgid "Configure File URLs"
416
  msgstr ""
417
 
418
- #: builds/amazon-s3-and-cloudfront/view/settings.php:83
419
  msgid "Path"
420
  msgstr ""
421
 
422
- #: builds/amazon-s3-and-cloudfront/view/settings.php:85
423
  msgid "By default the path is the same as your local WordPress files:"
424
  msgstr ""
425
 
426
- #: builds/amazon-s3-and-cloudfront/view/settings.php:98
427
  msgid "Year/Month"
428
  msgstr ""
429
 
430
- #: builds/amazon-s3-and-cloudfront/view/settings.php:100
431
  msgid "Add the Year/Month in the URL."
432
  msgstr ""
433
 
434
- #: builds/amazon-s3-and-cloudfront/view/settings.php:106
435
  msgid "SSL:"
436
  msgstr ""
437
 
438
- #: builds/amazon-s3-and-cloudfront/view/settings.php:114
439
  msgid "Same as request"
440
  msgstr ""
441
 
442
- #: builds/amazon-s3-and-cloudfront/view/settings.php:115
443
  msgid "When the request is https://, use https:// for the file URL as well."
444
  msgstr ""
445
 
446
- #: builds/amazon-s3-and-cloudfront/view/settings.php:119
447
  msgid "Always SSL"
448
  msgstr ""
449
 
450
- #: builds/amazon-s3-and-cloudfront/view/settings.php:120
451
  msgid "Forces https:// to be used."
452
  msgstr ""
453
 
454
- #: builds/amazon-s3-and-cloudfront/view/settings.php:121
455
  msgid ""
456
  "You cannot use the \"Bucket as a subdomain\" domain option when using SSL."
457
  msgstr ""
458
 
459
- #: builds/amazon-s3-and-cloudfront/view/settings.php:125
460
  msgid "Always non-SSL"
461
  msgstr ""
462
 
463
- #: builds/amazon-s3-and-cloudfront/view/settings.php:126
464
  msgid "Forces http:// to be used."
465
  msgstr ""
466
 
467
- #: builds/amazon-s3-and-cloudfront/view/settings.php:132
468
  msgid "Advanced Options"
469
  msgstr ""
470
 
471
- #: builds/amazon-s3-and-cloudfront/view/settings.php:139
472
  msgid "Remove Files From Server"
473
  msgstr ""
474
 
475
- #: builds/amazon-s3-and-cloudfront/view/settings.php:140
476
  msgid "Once a file has been copied to S3, remove it from the local server."
477
  msgstr ""
478
 
479
- #: builds/amazon-s3-and-cloudfront/view/settings.php:148
480
  msgid "Object Versioning"
481
  msgstr ""
482
 
483
- #: builds/amazon-s3-and-cloudfront/view/settings.php:150
484
  msgid ""
485
  "Append a timestamp to the S3 file path. Recommended when using CloudFront so "
486
  "you don't have to worry about cache invalidation."
487
  msgstr ""
488
 
489
- #: builds/amazon-s3-and-cloudfront/view/settings.php:152
490
- #: builds/amazon-s3-and-cloudfront/view/settings.php:165
491
  msgid "More info"
492
  msgstr ""
493
 
494
- #: builds/amazon-s3-and-cloudfront/view/settings.php:162
495
  msgid "Far Future Expiration Header"
496
  msgstr ""
497
 
498
- #: builds/amazon-s3-and-cloudfront/view/settings.php:163
499
  msgid ""
500
  "Implements a \"Never Expire\" caching policy for browsers by setting an "
501
  "Expires header for 10 years in the future. Should be used in conjunction "
502
  "with object versioning above."
503
  msgstr ""
504
 
505
- #: builds/amazon-s3-and-cloudfront/view/settings.php:175
506
  msgid "Copy HiDPI (@2x) Images"
507
  msgstr ""
508
 
509
- #: builds/amazon-s3-and-cloudfront/view/settings.php:176
510
  #, php-format
511
  msgid ""
512
  "When uploading a file to S3, checks if there's a file of the same name with "
@@ -514,80 +540,80 @@ msgid ""
514
  "Retina 2x</a> plugin."
515
  msgstr ""
516
 
517
- #: builds/amazon-s3-and-cloudfront/view/settings.php:182
518
  msgid "Save Changes"
519
  msgstr ""
520
 
521
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:10
522
  msgid "Upload existing Media Library to S3"
523
  msgstr ""
524
 
525
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:11
526
  msgid "Find & replace file URLs in content"
527
  msgstr ""
528
 
529
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:12
530
  msgid "Manage S3 files in WordPress"
531
  msgstr ""
532
 
533
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:13
534
  msgid "Assets addon - Serve your CSS & JS from S3/CloudFront"
535
  msgstr ""
536
 
537
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:14
538
  msgid "WooCommerce addon"
539
  msgstr ""
540
 
541
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:15
542
  msgid "Easy Digital Downloads addon"
543
  msgstr ""
544
 
545
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:16
546
  msgid "PriorityExpert™ email support"
547
  msgstr ""
548
 
549
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:19
550
  msgid "Visit deliciousbrains.com &rarr;"
551
  msgstr ""
552
 
553
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:26
554
  msgid "Get 20% Off!"
555
  msgstr ""
556
 
557
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:29
558
  #, php-format
559
  msgid ""
560
  "Submit your name and email and we’ll send you a coupon for 20% off your "
561
  "upgrade."
562
  msgstr ""
563
 
564
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:33
565
  msgid "Your Email"
566
  msgstr ""
567
 
568
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:37
569
  msgid "First Name"
570
  msgstr ""
571
 
572
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:41
573
  msgid "Last Name"
574
  msgstr ""
575
 
576
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:48
577
  msgid "Send me the coupon"
578
  msgstr ""
579
 
580
- #: builds/amazon-s3-and-cloudfront/view/sidebar.php:52
581
  msgid ""
582
  "We promise we will not use your email for anything else and you can "
583
  "unsubscribe with 1-click anytime."
584
  msgstr ""
585
 
586
- #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:2
587
  msgid "As this is a free plugin, we do not provide support."
588
  msgstr ""
589
 
590
- #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:4
591
  #, php-format
592
  msgid ""
593
  "You may ask the WordPress community for help by posting to the <a href=\"%s"
@@ -595,14 +621,14 @@ msgid ""
595
  "to a few weeks and will likely be from a non-developer."
596
  msgstr ""
597
 
598
- #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:6
599
  #, php-format
600
  msgid ""
601
  "If you want a <strong>timely response via email from a developer</strong> "
602
  "who works on this plugin, <a href=\"%s\">upgrade</a> and send us an email."
603
  msgstr ""
604
 
605
- #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:8
606
  #, php-format
607
  msgid ""
608
  "If you've found a bug, please <a href=\"%s\">submit an issue on GitHub</a>."
8
  msgstr ""
9
  "Project-Id-Version: amazon-s3-and-cloudfront\n"
10
  "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n"
11
+ "POT-Creation-Date: 2015-08-27 20:11+0100\n"
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
17
  "Content-Type: text/plain; charset=UTF-8\n"
18
  "Content-Transfer-Encoding: 8bit\n"
19
 
20
+ #: classes/amazon-s3-and-cloudfront.php:84
21
  msgid "Offload S3"
22
  msgstr ""
23
 
24
+ #: classes/amazon-s3-and-cloudfront.php:85
25
  msgid "S3 and CloudFront"
26
  msgstr ""
27
 
28
+ #: classes/amazon-s3-and-cloudfront.php:522
29
  #, php-format
30
  msgid "File %s does not exist"
31
  msgstr ""
32
 
33
+ #: classes/amazon-s3-and-cloudfront.php:531
34
  #, php-format
35
  msgid "Mime type %s is not allowed"
36
  msgstr ""
37
 
38
+ #: classes/amazon-s3-and-cloudfront.php:608
39
  #, php-format
40
  msgid "Error uploading %s to S3: %s"
41
  msgstr ""
42
 
43
+ #: classes/amazon-s3-and-cloudfront.php:1259
44
  msgid "Cheatin&#8217; eh?"
45
  msgstr ""
46
 
47
+ #: classes/amazon-s3-and-cloudfront.php:1263
48
  msgid "You do not have sufficient permissions to access this page."
49
  msgstr ""
50
 
51
+ #: classes/amazon-s3-and-cloudfront.php:1269
52
  msgid "No bucket name provided."
53
  msgstr ""
54
 
55
+ #: classes/amazon-s3-and-cloudfront.php:1544
56
+ msgid "Error Getting Bucket Region"
57
+ msgstr ""
58
+
59
+ #: classes/amazon-s3-and-cloudfront.php:1545
60
  #, php-format
61
  msgid "There was an error attempting to get the region of the bucket %s: %s"
62
  msgstr ""
63
 
64
+ #: classes/amazon-s3-and-cloudfront.php:1665
65
  msgid ""
66
  "This is a test file to check if the user has write permission to S3. Delete "
67
  "me if found."
68
  msgstr ""
69
 
70
+ #: classes/amazon-s3-and-cloudfront.php:1697
71
  #, php-format
72
  msgid ""
73
  "There was an error attempting to check the permissions of the bucket %s: %s"
74
  msgstr ""
75
 
76
+ #: classes/amazon-s3-and-cloudfront.php:1755
77
  msgid "Error creating bucket"
78
  msgstr ""
79
 
80
+ #: classes/amazon-s3-and-cloudfront.php:1756
81
  msgid "Bucket name too short."
82
  msgstr ""
83
 
84
+ #: classes/amazon-s3-and-cloudfront.php:1757
85
  msgid "Bucket name too long."
86
  msgstr ""
87
 
88
+ #: classes/amazon-s3-and-cloudfront.php:1758
89
  msgid ""
90
  "Invalid character. Bucket names can contain lowercase letters, numbers, "
91
  "periods and hyphens."
92
  msgstr ""
93
 
94
+ #: classes/amazon-s3-and-cloudfront.php:1759
95
  msgid "Error saving bucket"
96
  msgstr ""
97
 
98
+ #: classes/amazon-s3-and-cloudfront.php:1760
99
  msgid "Error fetching buckets"
100
  msgstr ""
101
 
102
+ #: classes/amazon-s3-and-cloudfront.php:1761
103
  msgid "Error getting URL preview: "
104
  msgstr ""
105
 
106
+ #: classes/amazon-s3-and-cloudfront.php:1762
107
  msgid "The changes you made will be lost if you navigate away from this page"
108
  msgstr ""
109
 
110
+ #: classes/amazon-s3-and-cloudfront.php:1820
111
  msgid "Cheatin' eh?"
112
  msgstr ""
113
 
114
+ #: classes/amazon-s3-and-cloudfront.php:1923
115
  msgctxt "Show the media library tab"
116
  msgid "Media Library"
117
  msgstr ""
118
 
119
+ #: classes/amazon-s3-and-cloudfront.php:1924
120
  msgctxt "Show the support tab"
121
  msgid "Support"
122
  msgstr ""
123
 
124
+ #: classes/amazon-s3-and-cloudfront.php:2060
125
  #, php-format
126
  msgid "The file %s has been given %s permissions on Amazon S3."
127
  msgstr ""
128
 
129
+ #: classes/amazon-s3-and-cloudfront.php:2561
130
  msgid "Quick Start Guide"
131
  msgstr ""
132
 
133
+ #: classes/amazon-s3-and-cloudfront.php:2563
134
  #, php-format
135
  msgid ""
136
  "Looks like we don't have write access to this bucket. It's likely that the "
139
  "correctly."
140
  msgstr ""
141
 
142
+ #: classes/amazon-s3-and-cloudfront.php:2565
143
  #, php-format
144
  msgid ""
145
  "Looks like we don't have access to the buckets. It's likely that the user "
147
  "Please see our %s for instructions on setting up permissions correctly."
148
  msgstr ""
149
 
150
+ #: classes/as3cf-plugin-compatibility.php:357
151
  #, php-format
152
  msgid "There was an error attempting to download the file %s from S3: %s"
153
  msgstr ""
154
 
155
+ #: classes/as3cf-upgrade.php:279
156
  #, php-format
157
  msgid ""
158
+ "<strong>Running %s Update</strong> &mdash; We&#8217;re going through all the "
159
+ "Media Library items uploaded to S3 %s This will be done quietly in the "
 
 
 
160
  "background, processing a small batch of Media Library items every %d "
161
  "minutes. There should be no noticeable impact on your server&#8217;s "
162
  "performance."
163
  msgstr ""
164
 
165
+ #: classes/as3cf-upgrade.php:280
166
  msgid "Pause Update"
167
  msgstr ""
168
 
169
+ #: classes/as3cf-upgrade.php:284
170
+ #, php-format
171
  msgid ""
172
+ "<strong>%s Update Paused</strong> &mdash; Updating Media Library %s has been "
173
+ "paused."
174
  msgstr ""
175
 
176
+ #: classes/as3cf-upgrade.php:285
177
  msgid "Restart Update"
178
  msgstr ""
179
 
180
+ #: classes/as3cf-upgrade.php:288
181
+ #, php-format
182
  msgid ""
183
+ "<strong>Error Updating %s</strong> &mdash; We ran into some errors "
184
+ "attempting to update the %s for all your Media Library items that have been "
185
+ "uploaded to S3. Please check your error log for details."
186
  msgstr ""
187
 
188
+ #: classes/as3cf-upgrade.php:289
189
  msgid "Try Run It Again"
190
  msgstr ""
191
 
192
+ #: classes/as3cf-upgrade.php:391
193
  #, php-format
194
  msgid "Every %d Minutes"
195
  msgstr ""
196
 
197
+ #: classes/upgrades/as3cf-file-sizes.php:37
198
+ msgid ""
199
+ "and updating the metadata with the sizes of files that have been removed "
200
+ "from the server. This will allow us to serve the correct size for media "
201
+ "items and the total space used in Multisite subsites."
202
+ msgstr ""
203
+
204
+ #: classes/upgrades/as3cf-region-meta.php:36
205
+ msgid ""
206
+ "and updating the metadata with the bucket region it is served from. This "
207
+ "will allow us to serve your files from the proper S3 region subdomain <span "
208
+ "style=\"white-space:nowrap;\">(e.g. s3-us-west-2.amazonaws.com)</span>."
209
+ msgstr ""
210
+
211
+ #: classes/wp-aws-compatibility-check.php:310
212
  msgid "deactivate"
213
  msgstr ""
214
 
215
+ #: classes/wp-aws-compatibility-check.php:311
216
  #, php-format
217
  msgid "You can %s the %s plugin to get rid of this notice."
218
  msgstr ""
219
 
220
+ #: classes/wp-aws-compatibility-check.php:314
221
  #, php-format
222
  msgid "%s has been disabled as it requires the %s plugin."
223
  msgstr ""
224
 
225
+ #: classes/wp-aws-compatibility-check.php:318
226
  msgid "which is currently disabled."
227
  msgstr ""
228
 
229
+ #: classes/wp-aws-compatibility-check.php:320
230
  msgid "It appears to be installed already."
231
  msgstr ""
232
 
233
+ #: classes/wp-aws-compatibility-check.php:322
234
  msgctxt "Activate plugin"
235
  msgid "Activate it now."
236
  msgstr ""
237
 
238
+ #: classes/wp-aws-compatibility-check.php:329
239
  #, php-format
240
  msgid "<a href=\"%s\">Install</a> and activate it."
241
  msgstr ""
242
 
243
+ #: classes/wp-aws-compatibility-check.php:340
244
  #, php-format
245
  msgid ""
246
  "%s has been disabled as it requires version %s or later of the %s plugin."
247
  msgstr ""
248
 
249
+ #: classes/wp-aws-compatibility-check.php:343
250
  #, php-format
251
  msgid "You currently have version %s installed."
252
  msgstr ""
253
 
254
+ #: classes/wp-aws-compatibility-check.php:350
255
  #, php-format
256
  msgid "A valid license for %s is required to update."
257
  msgstr ""
258
 
259
+ #: classes/wp-aws-compatibility-check.php:358
260
  msgid "Update to the latest version"
261
  msgstr ""
262
 
263
+ #: classes/wp-aws-compatibility-check.php:370
264
  #, php-format
265
  msgid ""
266
  "%1$s has been disabled because it is not a supported addon of the %2$s "
267
  "plugin."
268
  msgstr ""
269
 
270
+ #: classes/wp-aws-compatibility-check.php:379
271
  #, php-format
272
  msgid ""
273
  "%1$s has been disabled because it will not work with the version of the %2$s "
274
  "plugin installed. %1$s %3$s or later is required."
275
  msgstr ""
276
 
277
+ #: classes/wp-aws-compatibility-check.php:382
278
  #, php-format
279
  msgid "Update %s to the latest version"
280
  msgstr ""
281
 
282
+ #: classes/wp-aws-compatibility-check.php:442
283
  #, php-format
284
  msgid "The %s plugin has been deactivated."
285
  msgstr ""
286
 
287
+ #: view/bucket-select.php:3
288
  msgid "Change bucket"
289
  msgstr ""
290
 
291
+ #: view/bucket-select.php:3
292
  msgid "What bucket would you like to use?"
293
  msgstr ""
294
 
295
+ #: view/bucket-select.php:5
296
  msgid "Existing bucket name"
297
  msgstr ""
298
 
299
+ #: view/bucket-select.php:7
300
+ #: view/bucket-select.php:12
301
  msgid "Saving..."
302
  msgstr ""
303
 
304
+ #: view/bucket-select.php:7
305
+ #: view/bucket-select.php:12
306
  msgid "Save Bucket"
307
  msgstr ""
308
 
309
+ #: view/bucket-select.php:8
310
  msgid "Browse existing buckets"
311
  msgstr ""
312
 
313
+ #: view/bucket-select.php:9
314
+ #: view/bucket-select.php:26
315
+ #: view/bucket-select.php:31
316
  msgid "Create new bucket"
317
  msgstr ""
318
 
319
+ #: view/bucket-select.php:13
320
+ #: view/bucket-select.php:21
321
+ #: view/bucket-select.php:67
322
  msgid "Cancel"
323
  msgstr ""
324
 
325
+ #: view/bucket-select.php:18
326
  msgid "Select bucket"
327
  msgstr ""
328
 
329
+ #: view/bucket-select.php:19
330
  msgid "Loading..."
331
  msgstr ""
332
 
333
+ #: view/bucket-select.php:22
334
+ #: view/bucket-select.php:27
335
  msgid "Refresh"
336
  msgstr ""
337
 
338
+ #: view/bucket-select.php:25
339
  msgid "Enter bucket name"
340
  msgstr ""
341
 
342
+ #: view/bucket-select.php:37
343
  msgid "Bucket Name:"
344
  msgstr ""
345
 
346
+ #: view/bucket-select.php:40
347
  msgid "Bucket Name"
348
  msgstr ""
349
 
350
+ #: view/bucket-select.php:46
351
  msgid "Region:"
352
  msgstr ""
353
 
354
+ #: view/bucket-select.php:60
355
+ #, php-format
356
+ msgid "%s (defined in wp-config.php)"
357
+ msgstr ""
358
+
359
+ #: view/bucket-select.php:66
360
  msgid "Creating..."
361
  msgstr ""
362
 
363
+ #: view/bucket-select.php:66
364
  msgid "Create New Bucket"
365
  msgstr ""
366
 
367
+ #: view/bucket-setting.php:7
368
  msgid "Bucket"
369
  msgstr ""
370
 
371
+ #: view/bucket-setting.php:11
372
  msgid "Change"
373
  msgstr ""
374
 
375
+ #: view/bucket-setting.php:13
376
+ msgid "(defined in wp-config.php)"
377
+ msgstr ""
378
+
379
+ #: view/debug-info.php:2
380
  msgid "Diagnostic Info"
381
  msgstr ""
382
 
383
+ #: view/debug-info.php:11
384
  msgctxt "Download to your computer"
385
  msgid "Download"
386
  msgstr ""
387
 
388
+ #: view/domain-setting.php:5
389
  msgid "Domain:"
390
  msgstr ""
391
 
392
+ #: view/domain-setting.php:23
393
  msgid "Bucket name as subdomain"
394
  msgstr ""
395
 
396
+ #: view/domain-setting.php:28
397
  msgid "Bucket name in path"
398
  msgstr ""
399
 
400
+ #: view/domain-setting.php:33
401
  msgid "Bucket name as domain"
402
  msgstr ""
403
 
404
+ #: view/domain-setting.php:38
405
  msgid "CloudFront or custom domain"
406
  msgstr ""
407
 
408
+ #: view/error-access.php:4
409
  msgid "Access Denied to Bucket"
410
  msgstr ""
411
 
412
+ #: view/settings.php:10
413
  msgid "Settings saved."
414
  msgstr ""
415
 
416
+ #: view/settings.php:44
417
  msgid "Enable/Disable the Plugin"
418
  msgstr ""
419
 
420
+ #: view/settings.php:51
421
  msgid "Copy Files to S3"
422
  msgstr ""
423
 
424
+ #: view/settings.php:52
425
  msgid ""
426
  "When a file is uploaded to the Media Library, copy it to S3. Existing files "
427
  "are <em>not</em> copied to S3."
428
  msgstr ""
429
 
430
+ #: view/settings.php:60
431
  msgid "Rewrite File URLs"
432
  msgstr ""
433
 
434
+ #: view/settings.php:61
435
  msgid ""
436
  "For Media Library files that have been copied to S3, rewrite the URLs so "
437
  "that they are served from S3/CloudFront instead of your server."
438
  msgstr ""
439
 
440
+ #: view/settings.php:65
441
  msgid "Configure File URLs"
442
  msgstr ""
443
 
444
+ #: view/settings.php:83
445
  msgid "Path"
446
  msgstr ""
447
 
448
+ #: view/settings.php:85
449
  msgid "By default the path is the same as your local WordPress files:"
450
  msgstr ""
451
 
452
+ #: view/settings.php:98
453
  msgid "Year/Month"
454
  msgstr ""
455
 
456
+ #: view/settings.php:100
457
  msgid "Add the Year/Month in the URL."
458
  msgstr ""
459
 
460
+ #: view/settings.php:106
461
  msgid "SSL:"
462
  msgstr ""
463
 
464
+ #: view/settings.php:114
465
  msgid "Same as request"
466
  msgstr ""
467
 
468
+ #: view/settings.php:115
469
  msgid "When the request is https://, use https:// for the file URL as well."
470
  msgstr ""
471
 
472
+ #: view/settings.php:119
473
  msgid "Always SSL"
474
  msgstr ""
475
 
476
+ #: view/settings.php:120
477
  msgid "Forces https:// to be used."
478
  msgstr ""
479
 
480
+ #: view/settings.php:121
481
  msgid ""
482
  "You cannot use the \"Bucket as a subdomain\" domain option when using SSL."
483
  msgstr ""
484
 
485
+ #: view/settings.php:125
486
  msgid "Always non-SSL"
487
  msgstr ""
488
 
489
+ #: view/settings.php:126
490
  msgid "Forces http:// to be used."
491
  msgstr ""
492
 
493
+ #: view/settings.php:132
494
  msgid "Advanced Options"
495
  msgstr ""
496
 
497
+ #: view/settings.php:139
498
  msgid "Remove Files From Server"
499
  msgstr ""
500
 
501
+ #: view/settings.php:140
502
  msgid "Once a file has been copied to S3, remove it from the local server."
503
  msgstr ""
504
 
505
+ #: view/settings.php:148
506
  msgid "Object Versioning"
507
  msgstr ""
508
 
509
+ #: view/settings.php:150
510
  msgid ""
511
  "Append a timestamp to the S3 file path. Recommended when using CloudFront so "
512
  "you don't have to worry about cache invalidation."
513
  msgstr ""
514
 
515
+ #: view/settings.php:152
516
+ #: view/settings.php:165
517
  msgid "More info"
518
  msgstr ""
519
 
520
+ #: view/settings.php:162
521
  msgid "Far Future Expiration Header"
522
  msgstr ""
523
 
524
+ #: view/settings.php:163
525
  msgid ""
526
  "Implements a \"Never Expire\" caching policy for browsers by setting an "
527
  "Expires header for 10 years in the future. Should be used in conjunction "
528
  "with object versioning above."
529
  msgstr ""
530
 
531
+ #: view/settings.php:175
532
  msgid "Copy HiDPI (@2x) Images"
533
  msgstr ""
534
 
535
+ #: view/settings.php:176
536
  #, php-format
537
  msgid ""
538
  "When uploading a file to S3, checks if there's a file of the same name with "
540
  "Retina 2x</a> plugin."
541
  msgstr ""
542
 
543
+ #: view/settings.php:182
544
  msgid "Save Changes"
545
  msgstr ""
546
 
547
+ #: view/sidebar.php:10
548
  msgid "Upload existing Media Library to S3"
549
  msgstr ""
550
 
551
+ #: view/sidebar.php:11
552
  msgid "Find & replace file URLs in content"
553
  msgstr ""
554
 
555
+ #: view/sidebar.php:12
556
  msgid "Manage S3 files in WordPress"
557
  msgstr ""
558
 
559
+ #: view/sidebar.php:13
560
  msgid "Assets addon - Serve your CSS & JS from S3/CloudFront"
561
  msgstr ""
562
 
563
+ #: view/sidebar.php:14
564
  msgid "WooCommerce addon"
565
  msgstr ""
566
 
567
+ #: view/sidebar.php:15
568
  msgid "Easy Digital Downloads addon"
569
  msgstr ""
570
 
571
+ #: view/sidebar.php:16
572
  msgid "PriorityExpert™ email support"
573
  msgstr ""
574
 
575
+ #: view/sidebar.php:19
576
  msgid "Visit deliciousbrains.com &rarr;"
577
  msgstr ""
578
 
579
+ #: view/sidebar.php:26
580
  msgid "Get 20% Off!"
581
  msgstr ""
582
 
583
+ #: view/sidebar.php:29
584
  #, php-format
585
  msgid ""
586
  "Submit your name and email and we’ll send you a coupon for 20% off your "
587
  "upgrade."
588
  msgstr ""
589
 
590
+ #: view/sidebar.php:33
591
  msgid "Your Email"
592
  msgstr ""
593
 
594
+ #: view/sidebar.php:37
595
  msgid "First Name"
596
  msgstr ""
597
 
598
+ #: view/sidebar.php:41
599
  msgid "Last Name"
600
  msgstr ""
601
 
602
+ #: view/sidebar.php:48
603
  msgid "Send me the coupon"
604
  msgstr ""
605
 
606
+ #: view/sidebar.php:52
607
  msgid ""
608
  "We promise we will not use your email for anything else and you can "
609
  "unsubscribe with 1-click anytime."
610
  msgstr ""
611
 
612
+ #: view/wordpress-org-support.php:2
613
  msgid "As this is a free plugin, we do not provide support."
614
  msgstr ""
615
 
616
+ #: view/wordpress-org-support.php:4
617
  #, php-format
618
  msgid ""
619
  "You may ask the WordPress community for help by posting to the <a href=\"%s"
621
  "to a few weeks and will likely be from a non-developer."
622
  msgstr ""
623
 
624
+ #: view/wordpress-org-support.php:6
625
  #, php-format
626
  msgid ""
627
  "If you want a <strong>timely response via email from a developer</strong> "
628
  "who works on this plugin, <a href=\"%s\">upgrade</a> and send us an email."
629
  msgstr ""
630
 
631
+ #: view/wordpress-org-support.php:8
632
  #, php-format
633
  msgid ""
634
  "If you've found a bug, please <a href=\"%s\">submit an issue on GitHub</a>."
languages/as3cf-pt-br.mo CHANGED
Binary file
readme.txt CHANGED
@@ -1,10 +1,9 @@
1
  === WP Offload S3 ===
2
- Contributors: bradt
3
- Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5VPMGLLK94XJC
4
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  Requires at least: 3.7
6
  Tested up to: 4.3
7
- Stable tag: 0.9.3
8
  License: GPLv3
9
 
10
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
@@ -17,19 +16,21 @@ This plugin automatically copies images, videos, documents, and any other media
17
 
18
  Uploading files *directly* to your S3 account is not currently supported by this plugin. They are uploaded to your server first, then copied to S3. There is an option to automatically remove the files from your server once they are copied to S3 however.
19
 
20
- If you're adding this plugin to a site that's been around for a while, your existing media files will not be copied or served from S3. Only newly uploaded files will be copied and served from S3.
21
 
22
  **PRO Upgrade with Email Support and More Features**
23
 
24
  * Upload existing Media Library to S3
25
  * Find & replace file URLs in content
26
  * Control S3 files from the Media Library
27
- * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#assets-addon) - Serve your CSS & JS from S3/CloudFront
28
- * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#woocommerce-addon)
29
- * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin#edd-addon)
30
  * PriorityExpert&trade; email support
31
 
32
- See the video below or [visit the web site](http://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=freeplugin) to learn more about the pro version.
 
 
33
 
34
  https://www.youtube.com/watch?v=55xNGnbJ_CY
35
 
@@ -62,6 +63,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
62
 
63
  == Changelog ==
64
 
 
 
 
 
 
 
 
 
 
 
 
65
  = 0.9.3 - 2015-08-17 =
66
  * New: Pro upgrade sidebar
67
  * Bug fix: Create buckets in US standard region causing S3 URLs to 404 errors
1
  === WP Offload S3 ===
2
+ Contributors: bradt, deliciousbrains
 
3
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
4
  Requires at least: 3.7
5
  Tested up to: 4.3
6
+ Stable tag: 0.9.4
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.
16
 
17
  Uploading files *directly* to your S3 account is not currently supported by this plugin. They are uploaded to your server first, then copied to S3. There is an option to automatically remove the files from your server once they are copied to S3 however.
18
 
19
+ If you're adding this plugin to a site that's been around for a while, your existing media files will not be copied or served from S3. Only newly uploaded files will be copied and served from S3. The pro upgrade has an upload tool to handle existing media files.
20
 
21
  **PRO Upgrade with Email Support and More Features**
22
 
23
  * Upload existing Media Library to S3
24
  * Find & replace file URLs in content
25
  * Control 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 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)
28
+ * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#edd-addon)
29
  * PriorityExpert&trade; email support
30
 
31
+ [Compare pro vs free &rarr;](http://deliciousbrains.com/wp-offload-s3/upgrade/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin)
32
+
33
+ The video below runs through the pro upgrade features...
34
 
35
  https://www.youtube.com/watch?v=55xNGnbJ_CY
36
 
63
 
64
  == Changelog ==
65
 
66
+ = 0.9.4 - 2015-08-27 =
67
+ * New: Update all existing attachments with missing file sizes when the 'Remove Files From Server' option is enabled (automatically runs in the background)
68
+ * Improvement: Show when constants are used to set bucket and region options
69
+ * Improvement: Don't show compatibility notices on plugin update screen
70
+ * Improvement: On Multisite installs don't call `restore_current_blog()` on successive loop iterations
71
+ * Bug fix: 'Error getting URL preview' alert shown when enter key pressed on settings screen
72
+ * Bug fix: Unable to crop header images when the 'Remove Files From Server' option is enabled
73
+ * Bug fix: Incorrect storage space shown on Multisite installs when the 'Remove Files From Server' option is enabled
74
+ * Bug fix: Upload attempted to non existent bucket when defined by constant
75
+ * Bug fix: 'SignatureDoesNotMatch' error shown when using signed URLs with bucket names containing '.' characters
76
+
77
  = 0.9.3 - 2015-08-17 =
78
  * New: Pro upgrade sidebar
79
  * Bug fix: Create buckets in US standard region causing S3 URLs to 404 errors
uninstall.php CHANGED
@@ -16,9 +16,28 @@ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
16
 
17
  require dirname( __FILE__ ) . '/classes/wp-aws-uninstall.php';
18
 
19
- $options = 'tantan_wordpress_s3';
20
- $postmeta = 'amazonS3_info';
21
- $crons = 'as3cf_cron_update_meta_with_region';
22
- $transients = 'as3cf_notices';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  $as3cf_uninstall = new WP_AWS_Uninstall( $options, $postmeta, $crons, $transients );
16
 
17
  require dirname( __FILE__ ) . '/classes/wp-aws-uninstall.php';
18
 
19
+ $options = array(
20
+ 'tantan_wordpress_s3',
21
+ 'update_meta_with_region_session',
22
+ 'update_file_sizes_session',
23
+ );
24
+
25
+ $postmeta = array(
26
+ 'amazonS3_info',
27
+ 'wpos3_filesize_total',
28
+ );
29
+
30
+ $crons = array(
31
+ 'as3cf_cron_update_meta_with_region',
32
+ 'as3cf_cron_update_file_sizes',
33
+ );
34
+
35
+ $transients = array(
36
+ 'site' => array(
37
+ 'as3cf_notices',
38
+ 'wpos3_attachment_counts',
39
+ ),
40
+ 'subsite' => array( 'wpos3_site_space_used' ),
41
+ );
42
 
43
  $as3cf_uninstall = new WP_AWS_Uninstall( $options, $postmeta, $crons, $transients );
view/bucket-select.php CHANGED
@@ -41,20 +41,26 @@
41
  <p class="as3cf-invalid-bucket-name"></p>
42
  </td>
43
  </tr>
44
- <?php if ( ! defined( 'AS3CF_REGION' ) ) : ?>
45
- <tr>
46
- <td>
47
- <?php _e( 'Region:', 'as3cf' ); ?>
48
- </td>
49
- <td>
 
 
50
  <select class="bucket-create-region" name="region_name">
51
- <?php foreach ( $this->get_aws_regions() as $value => $label ) : ?>
52
  <option value="<?php echo $value; ?>"> <?php echo $label; ?></option>
53
  <?php endforeach; ?>
54
  </select>
55
- </td>
56
- </tr>
57
- <?php endif; ?>
 
 
 
 
58
  </table>
59
  <p class="bucket-actions actions">
60
  <button type="submit" class="button button-primary" data-working="<?php _e( 'Creating...', 'as3cf' ); ?>"><?php _e( 'Create New Bucket', 'as3cf' ); ?></button>
41
  <p class="as3cf-invalid-bucket-name"></p>
42
  </td>
43
  </tr>
44
+ <tr>
45
+ <td>
46
+ <?php _e( 'Region:', 'as3cf' ); ?>
47
+ </td>
48
+ <td>
49
+ <?php
50
+ $aws_regions = $this->get_aws_regions();
51
+ if ( ! defined( 'AS3CF_REGION' ) ) { ?>
52
  <select class="bucket-create-region" name="region_name">
53
+ <?php foreach ( $aws_regions as $value => $label ) : ?>
54
  <option value="<?php echo $value; ?>"> <?php echo $label; ?></option>
55
  <?php endforeach; ?>
56
  </select>
57
+ <?php } else {
58
+ $region = AS3CF_REGION;
59
+ $region_name = isset( $aws_regions[ $region ] ) ? $aws_regions[ $region ] : $region;
60
+ printf( __( '%s (defined in wp-config.php)', 'as3cf' ), $region_name );
61
+ } ?>
62
+ </td>
63
+ </tr>
64
  </table>
65
  <p class="bucket-actions actions">
66
  <button type="submit" class="button button-primary" data-working="<?php _e( 'Creating...', 'as3cf' ); ?>"><?php _e( 'Create New Bucket', 'as3cf' ); ?></button>
view/bucket-setting.php CHANGED
@@ -7,9 +7,11 @@ $tr_class = ( isset( $tr_class ) ) ? $tr_class : '';
7
  <td><h3><?php _e( 'Bucket', 'as3cf' ); ?></h3></td>
8
  <td>
9
  <span id="<?php echo $prefix; ?>-active-bucket" class="as3cf-active-bucket"><?php echo $selected_bucket; // xss ok ?></span>
10
- <?php if ( ! defined( $constant ) ) : ?>
11
  <a href="#" class="as3cf-change-bucket" data-as3cf-modal=".as3cf-bucket-container"><?php _e( 'Change', 'as3cf' ); ?></a>
12
- <?php endif; ?>
 
 
13
  <input id="<?php echo $prefix; ?>-bucket" type="hidden" class="no-compare" name="bucket" value="<?php echo esc_attr( $selected_bucket ); ?>">
14
  <?php
15
  $region = $this->get_setting( 'region' );
7
  <td><h3><?php _e( 'Bucket', 'as3cf' ); ?></h3></td>
8
  <td>
9
  <span id="<?php echo $prefix; ?>-active-bucket" class="as3cf-active-bucket"><?php echo $selected_bucket; // xss ok ?></span>
10
+ <?php if ( ! defined( $constant ) ) { ?>
11
  <a href="#" class="as3cf-change-bucket" data-as3cf-modal=".as3cf-bucket-container"><?php _e( 'Change', 'as3cf' ); ?></a>
12
+ <?php } else {
13
+ _e( '(defined in wp-config.php)', 'as3cf' );
14
+ } ?>
15
  <input id="<?php echo $prefix; ?>-bucket" type="hidden" class="no-compare" name="bucket" value="<?php echo esc_attr( $selected_bucket ); ?>">
16
  <?php
17
  $region = $this->get_setting( 'region' );
view/sidebar.php CHANGED
@@ -56,12 +56,6 @@
56
  <div class="block credits">
57
  <h4>Created &amp; maintained by</h4>
58
  <ul>
59
- <li>
60
- <a href="http://profiles.wordpress.org/bradt/">
61
- <img src="//www.gravatar.com/avatar/e538ca4cb34839d4e5e3ccf20c37c67b?size=64" alt="" width="32" height="32">
62
- <span>Brad Touesnard</span>
63
- </a>
64
- </li>
65
  <li>
66
  <a href="https://deliciousbrains.com/?utm_source=insideplugin&amp;utm_medium=web&amp;utm_content=sidebar&amp;utm_campaign=os3-free-plugin">
67
  <img src="//www.gravatar.com/avatar/e62fc2e9c8d9fc6edd4fea5339036a91?size=64" alt="" width="32" height="32">
56
  <div class="block credits">
57
  <h4>Created &amp; maintained by</h4>
58
  <ul>
 
 
 
 
 
 
59
  <li>
60
  <a href="https://deliciousbrains.com/?utm_source=insideplugin&amp;utm_medium=web&amp;utm_content=sidebar&amp;utm_campaign=os3-free-plugin">
61
  <img src="//www.gravatar.com/avatar/e62fc2e9c8d9fc6edd4fea5339036a91?size=64" alt="" width="32" height="32">
wordpress-s3.php CHANGED
@@ -3,14 +3,14 @@
3
  Plugin Name: WP Offload S3
4
  Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/
5
  Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery.
6
- Author: Brad Touesnard
7
- Version: 0.9.3
8
- Author URI: http://bradt.ca
9
  Network: True
10
  Text Domain: as3cf
11
  Domain Path: /languages/
12
 
13
- // Copyright (c) 2013 Brad Touesnard. All rights reserved.
14
  //
15
  // Released under the GPL license
16
  // http://www.opensource.org/licenses/gpl-license.php
@@ -26,7 +26,7 @@ Domain Path: /languages/
26
  // Then completely rewritten.
27
  */
28
 
29
- $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '0.9.3';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
  'amazon-s3-and-cloudfront-pro' => '1.0b1',
@@ -55,6 +55,8 @@ function as3cf_init( $aws ) {
55
  $abspath = dirname( __FILE__ );
56
  require_once $abspath . '/include/functions.php';
57
  require_once $abspath . '/classes/as3cf-upgrade.php';
 
 
58
  require_once $abspath . '/classes/as3cf-plugin-compatibility.php';
59
  require_once $abspath . '/classes/amazon-s3-and-cloudfront.php';
60
  $as3cf = new Amazon_S3_And_CloudFront( __FILE__, $aws );
3
  Plugin Name: WP Offload S3
4
  Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/
5
  Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery.
6
+ Author: Delicious Brains
7
+ Version: 0.9.4
8
+ Author URI: http://deliciousbrains.com/
9
  Network: True
10
  Text Domain: as3cf
11
  Domain Path: /languages/
12
 
13
+ // Copyright (c) 2013 Delicious Brains. All rights reserved.
14
  //
15
  // Released under the GPL license
16
  // http://www.opensource.org/licenses/gpl-license.php
26
  // Then completely rewritten.
27
  */
28
 
29
+ $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '0.9.4';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
  'amazon-s3-and-cloudfront-pro' => '1.0b1',
55
  $abspath = dirname( __FILE__ );
56
  require_once $abspath . '/include/functions.php';
57
  require_once $abspath . '/classes/as3cf-upgrade.php';
58
+ require_once $abspath . '/classes/upgrades/as3cf-region-meta.php';
59
+ require_once $abspath . '/classes/upgrades/as3cf-file-sizes.php';
60
  require_once $abspath . '/classes/as3cf-plugin-compatibility.php';
61
  require_once $abspath . '/classes/amazon-s3-and-cloudfront.php';
62
  $as3cf = new Amazon_S3_And_CloudFront( __FILE__, $aws );