WP Offload S3 Lite - Version 1.2

Version Description

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

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

Download this release

Release Info

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

Code changes from version 1.1.6 to 1.2

Files changed (34) hide show
  1. README.md +22 -11
  2. assets/js/modal.js +4 -4
  3. assets/js/modal.min.js +1 -1
  4. assets/js/script.js +32 -42
  5. assets/js/script.min.js +1 -1
  6. classes/amazon-s3-and-cloudfront.php +190 -258
  7. classes/as3cf-filter.php +212 -41
  8. classes/as3cf-notices.php +2 -9
  9. classes/as3cf-plugin-compatibility.php +54 -211
  10. classes/as3cf-upgrade.php +0 -600
  11. classes/as3cf-utils.php +264 -1
  12. classes/filters/as3cf-local-to-s3.php +13 -40
  13. classes/filters/as3cf-s3-to-local.php +7 -23
  14. classes/upgrades/exceptions/batch-limits-exceeded-exception.php +7 -0
  15. classes/upgrades/exceptions/no-more-blogs-exception.php +7 -0
  16. classes/upgrades/exceptions/too-many-errors-exception.php +7 -0
  17. classes/upgrades/network-upgrade.php +95 -0
  18. classes/upgrades/{as3cf-filter-post-content.php → upgrade-content-replace-urls.php} +12 -15
  19. classes/upgrades/{as3cf-filter-edd.php → upgrade-edd-replace-urls.php} +8 -27
  20. classes/upgrades/{as3cf-file-sizes.php → upgrade-file-sizes.php} +25 -66
  21. classes/upgrades/{as3cf-filter-post-excerpt.php → upgrade-filter-post-excerpt.php} +3 -6
  22. classes/{as3cf-upgrade-filter-post.php → upgrades/upgrade-filter-post.php} +106 -242
  23. classes/upgrades/{as3cf-meta-wp-error.php → upgrade-meta-wp-error.php} +10 -22
  24. classes/upgrades/{as3cf-region-meta.php → upgrade-region-meta.php} +9 -20
  25. classes/upgrades/upgrade.php +939 -0
  26. languages/amazon-s3-and-cloudfront-en.pot +125 -146
  27. readme.txt +22 -11
  28. view/bucket-setting.php +9 -1
  29. view/domain-setting.php +1 -1
  30. view/settings-tabs.php +2 -3
  31. view/settings.php +8 -8
  32. view/sidebar.php +15 -4
  33. view/wordpress-org-support.php +5 -1
  34. wordpress-s3.php +4 -12
README.md CHANGED
@@ -1,9 +1,9 @@
1
  # WP Offload S3 Lite #
2
  **Contributors:** bradt, deliciousbrains
3
  **Tags:** uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
- **Requires at least:** 4.4
5
- **Tested up to:** 4.7.3
6
- **Stable tag:** 1.1.6
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.
@@ -22,12 +22,12 @@ If you're adding this plugin to a site that's been around for a while, your exis
22
 
23
  * Upload existing Media Library to Amazon S3
24
  * Control Amazon S3 files from the Media Library
25
- * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#assets-addon) - Serve your CSS & JS from Amazon S3/CloudFront
26
- * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#woocommerce-addon)
27
- * [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)
28
  * PriorityExpert™ email support
29
 
30
- [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)
31
 
32
  The video below runs through the pro upgrade features...
33
 
@@ -48,7 +48,7 @@ which is a fork of [Amazon S3 for WordPress](http://wordpress.org/extend/plugins
48
 
49
  ### What are the minimum requirements? ###
50
 
51
- You can see the minimum requirements [here](https://deliciousbrains.com/wp-offload-s3/pricing/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#requirements).
52
 
53
  ## Screenshots ##
54
 
@@ -69,6 +69,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
69
 
70
  ## Changelog ##
71
 
 
 
 
 
 
 
 
 
 
 
 
72
  ### WP Offload S3 Lite 1.1.6 - 2017-03-13 ###
73
  * New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/)
74
  * New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content
@@ -118,7 +129,7 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
118
 
119
  ### WP Offload S3 Lite 1.0.5 - 2016-09-01 ###
120
  * New: Compatibility with WordPress 4.6
121
- * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/)
122
 
123
  ### WP Offload S3 Lite 1.0.4 - 2016-05-30 ###
124
  * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
@@ -224,8 +235,8 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
224
  * Bug fix: Accidentally released the sidebar for after we launch the pro version
225
 
226
  ### WP Offload S3 0.9.1 - 2015-07-29 ###
227
- * Improvement: Access denied sample IAM policy replaced with link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/)
228
- * Improvement: Access denied messages on bucket selection or bucket creation now link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/)
229
  * Improvement: Object expires time can now be filtered using the `as3cf_object_meta` filter
230
  * Bug fix: Error not always shown when S3 bucket inaccessible due to incorrect permissions
231
  * Bug fix: Permission checks fail when S3 bucket is in a non-default region and defined by `AS3CF_BUCKET` constant
1
  # WP Offload S3 Lite #
2
  **Contributors:** bradt, deliciousbrains
3
  **Tags:** uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
+ **Requires at least:** 4.6
5
+ **Tested up to:** 4.8
6
+ **Stable tag:** 1.2
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.
22
 
23
  * Upload existing Media Library to Amazon S3
24
  * Control Amazon S3 files from the Media Library
25
+ * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=assets%2Baddon#addons) - Serve your CSS & JS from Amazon S3/CloudFront
26
+ * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=woocommerce%2Baddon#addons)
27
+ * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=edd%2Baddon#addons)
28
  * PriorityExpert™ email support
29
 
30
+ [Compare pro vs free →](https://deliciousbrains.com/wp-offload-s3/upgrade/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
31
 
32
  The video below runs through the pro upgrade features...
33
 
48
 
49
  ### What are the minimum requirements? ###
50
 
51
+ You can see the minimum requirements [here](https://deliciousbrains.com/wp-offload-s3/pricing/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=requirements#requirements).
52
 
53
  ## Screenshots ##
54
 
69
 
70
  ## Changelog ##
71
 
72
+ ### WP Offload S3 Lite 1.2 - 2017-06-19 ###
73
+ * New: Compatibility with WordPress 4.8
74
+ * New: Support for WP CLI `wp media regenerate`
75
+ * Improvement: Intermediate image sizes are now passed through the `as3cf_object_meta` filter
76
+ * Improvement: Content filtering cache now uses the external object when available
77
+ * Bug fix: Timeouts on large multisite installs due to excessive database queries on upgrade routines
78
+ * Bug fix: Video files with private ACL not working with WordPress's default media player
79
+ * Bug fix: Bucket permissions check not using configured path
80
+ * Bug fix: WordPress image editor sometimes shows a 404 when 'Remove Files From Server' enabled
81
+ * Bug fix: Notice: Undefined index: region
82
+
83
  ### WP Offload S3 Lite 1.1.6 - 2017-03-13 ###
84
  * New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/)
85
  * New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content
129
 
130
  ### WP Offload S3 Lite 1.0.5 - 2016-09-01 ###
131
  * New: Compatibility with WordPress 4.6
132
+ * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
133
 
134
  ### WP Offload S3 Lite 1.0.4 - 2016-05-30 ###
135
  * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
235
  * Bug fix: Accidentally released the sidebar for after we launch the pro version
236
 
237
  ### WP Offload S3 0.9.1 - 2015-07-29 ###
238
+ * Improvement: Access denied sample IAM policy replaced with link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
239
+ * Improvement: Access denied messages on bucket selection or bucket creation now link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
240
  * Improvement: Object expires time can now be filtered using the `as3cf_object_meta` filter
241
  * Bug fix: Error not always shown when S3 bucket inaccessible due to incorrect permissions
242
  * Bug fix: Permission checks fail when S3 bucket is in a non-default region and defined by `AS3CF_BUCKET` constant
assets/js/modal.js CHANGED
@@ -101,13 +101,13 @@ var as3cfModal = (function( $ ) {
101
  var target = $( '#as3cf-modal' ).data( 'as3cf-modal-target' );
102
 
103
  $( '#as3cf-overlay' ).fadeOut( 150, function() {
104
- if ( 'function' === typeof callback ) {
105
- callback( target );
106
- }
107
-
108
  $( 'body' ).removeClass( 'as3cf-modal-open' );
109
 
110
  $( this ).remove();
 
 
 
 
111
  } );
112
 
113
  $( 'body' ).trigger( 'as3cf-modal-close', [ target ] );
101
  var target = $( '#as3cf-modal' ).data( 'as3cf-modal-target' );
102
 
103
  $( '#as3cf-overlay' ).fadeOut( 150, function() {
 
 
 
 
104
  $( 'body' ).removeClass( 'as3cf-modal-open' );
105
 
106
  $( this ).remove();
107
+
108
+ if ( 'function' === typeof callback ) {
109
+ callback( target );
110
+ }
111
  } );
112
 
113
  $( 'body' ).trigger( 'as3cf-modal-close', [ target ] );
assets/js/modal.min.js CHANGED
@@ -1 +1 @@
1
- var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf",loading:!1,dismissible:!0},d={};return c.exists=function(c){var e=b(c);return void 0!==d[e]||!!a(c).length},c.open=function(e,f,g){var h=b(e);a("body").append('<div id="as3cf-overlay"></div>');var i=a("#as3cf-overlay");c.dismissible?i.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>'):i.append('<div id="as3cf-modal"></div>');var j=a("#as3cf-modal");if(void 0===d[h]){var k=a(e);d[h]=k.clone(!0).css("display","block"),k.remove()}j.data("as3cf-modal-target",e).append(d[h]),void 0!==g&&j.addClass(g),"function"==typeof f&&f(e),a("body").addClass("as3cf-modal-open"),i.fadeIn(150),j.fadeIn(150),a("body").trigger("as3cf-modal-open",[e])},c.close=function(b){if(!c.loading&&c.dismissible){var d=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){"function"==typeof b&&b(d),a("body").removeClass("as3cf-modal-open"),a(this).remove()}),a("body").trigger("as3cf-modal-close",[d])}},c.setLoadingState=function(a){c.loading=a},c.setDismissibleState=function(a){c.dismissible=a},a(document).ready(function(){a("body").on("click","[data-as3cf-modal]",function(b){b.preventDefault(),c.open(a(this).data("as3cf-modal")+"."+c.prefix)}),a("body").on("click","#as3cf-overlay, .close-as3cf-modal",function(a){return a.preventDefault(),a.target===this&&void c.close()})}),c}(jQuery);
1
+ var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf",loading:!1,dismissible:!0},d={};return c.exists=function(c){var e=b(c);return void 0!==d[e]||!!a(c).length},c.open=function(e,f,g){var h=b(e);a("body").append('<div id="as3cf-overlay"></div>');var i=a("#as3cf-overlay");c.dismissible?i.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>'):i.append('<div id="as3cf-modal"></div>');var j=a("#as3cf-modal");if(void 0===d[h]){var k=a(e);d[h]=k.clone(!0).css("display","block"),k.remove()}j.data("as3cf-modal-target",e).append(d[h]),void 0!==g&&j.addClass(g),"function"==typeof f&&f(e),a("body").addClass("as3cf-modal-open"),i.fadeIn(150),j.fadeIn(150),a("body").trigger("as3cf-modal-open",[e])},c.close=function(b){if(!c.loading&&c.dismissible){var d=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){a("body").removeClass("as3cf-modal-open"),a(this).remove(),"function"==typeof b&&b(d)}),a("body").trigger("as3cf-modal-close",[d])}},c.setLoadingState=function(a){c.loading=a},c.setDismissibleState=function(a){c.dismissible=a},a(document).ready(function(){a("body").on("click","[data-as3cf-modal]",function(b){b.preventDefault(),c.open(a(this).data("as3cf-modal")+"."+c.prefix)}),a("body").on("click","#as3cf-overlay, .close-as3cf-modal",function(a){return a.preventDefault(),a.target===this&&void c.close()})}),c}(jQuery);
assets/js/script.js CHANGED
@@ -459,7 +459,12 @@
459
 
460
  setBucketLink();
461
 
462
- as3cfModal.close( unlockBucketSelect );
 
 
 
 
 
463
  },
464
 
465
  /**
@@ -623,15 +628,6 @@
623
  } );
624
  }
625
 
626
- /**
627
- * Reset the bucket select lock
628
- */
629
- function unlockBucketSelect( target ) {
630
-
631
- // Unlock setting the bucket
632
- as3cf.buckets.bucketSelectLock = false;
633
- }
634
-
635
  /*
636
  * Toggle the lost files notice
637
  */
@@ -654,47 +650,41 @@
654
  }
655
  }
656
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
  $( document ).ready( function() {
658
 
659
  // Tabs
660
  // --------------------
 
 
 
 
 
 
661
 
662
  // Move any compatibility errors below the nav tabs
663
  var $navTabs = $( '.wrap.aws-main .nav-tab-wrapper' );
664
  $( '.aws-compatibility-notice, div.updated, div.error, div.notice' ).not( '.below-h2, .inline' ).insertAfter( $navTabs );
665
 
666
- // Check for hash in url and switch tabs accordingly
667
- if ( window.location.hash ) {
668
- var hash = window.location.hash.substring( 1 );
669
- as3cf.tabs.toggle( hash, true );
670
- } else {
671
-
672
- // Default settings tab
673
- $activeTab = $( '#tab-' + as3cf.tabs.defaultTab );
674
- $( '.aws-main' ).attr( 'data-tab', as3cf.tabs.defaultTab );
675
- }
676
-
677
- $( '.aws-main' ).on( 'click', '.nav-tab', function( e ) {
678
- e.preventDefault();
679
- if ( $( this ).hasClass( 'nav-tab-active' ) ) {
680
- return;
681
- }
682
- var nextTab = $( this ).attr( 'data-tab' );
683
- as3cf.tabs.toggle( nextTab );
684
- if ( 'media' === nextTab ) {
685
-
686
- // As it's the default remove the hash
687
- window.location.hash = '';
688
- if ( 'function' === typeof window.history.replaceState && '#' === window.location.href.slice( -1 ) ) {
689
-
690
- // Strip the # if still on the end of the URL
691
- history.replaceState( {}, '', window.location.href.slice( 0, -1 ) );
692
- }
693
- } else {
694
- window.location.hash = nextTab;
695
- }
696
- } );
697
-
698
  // Settings
699
  // --------------------
700
 
459
 
460
  setBucketLink();
461
 
462
+ as3cfModal.close( function() {
463
+ $activeTab.trigger( 'bucket-change', [ canWrite ] );
464
+
465
+ // Unlock setting the bucket
466
+ as3cf.buckets.bucketSelectLock = false;
467
+ } );
468
  },
469
 
470
  /**
628
  } );
629
  }
630
 
 
 
 
 
 
 
 
 
 
631
  /*
632
  * Toggle the lost files notice
633
  */
650
  }
651
  }
652
 
653
+ /**
654
+ * Update the UI with the current active tab set in the URL hash.
655
+ */
656
+ function renderCurrentTab() {
657
+
658
+ // If rendering the default tab, or a bare hash clean the hash.
659
+ if ( '#' + as3cf.tabs.defaultTab === location.hash ) {
660
+ location.hash = '';
661
+
662
+ return;
663
+ }
664
+
665
+ // Strip the # if still on the end of the URL
666
+ if ( 'function' === typeof history.replaceState && '#' === location.href.slice( -1 ) ) {
667
+ history.replaceState( {}, '', location.href.slice( 0, -1 ) );
668
+ }
669
+
670
+ as3cf.tabs.toggle( location.hash.replace( '#', '' ), true );
671
+ }
672
+
673
  $( document ).ready( function() {
674
 
675
  // Tabs
676
  // --------------------
677
+ renderCurrentTab();
678
+
679
+ /**
680
+ * Set the hashchange callback to update the rendered active tab.
681
+ */
682
+ window.onhashchange = renderCurrentTab;
683
 
684
  // Move any compatibility errors below the nav tabs
685
  var $navTabs = $( '.wrap.aws-main .nav-tab-wrapper' );
686
  $( '.aws-compatibility-notice, div.updated, div.error, div.notice' ).not( '.below-h2, .inline' ).insertAfter( $navTabs );
687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  // Settings
689
  // --------------------
690
 
assets/js/script.min.js CHANGED
@@ -1 +1 @@
1
- !function(a,b){function c(b){return a("#"+b+" .as3cf-main-settings form").find("input:not(.no-compare)").serialize()}function d(a){var b=k.find("#"+a),c=b.find("input[type=checkbox]");b.toggleClass("on").find("span").toggleClass("checked");var d=b.find("span.on").hasClass("checked");c.attr("checked",d).trigger("change")}function e(b){var c=b.next(".as3cf-validation-error"),d=a("#"+k.attr("id")+' form button[type="submit"]'),e=/[^a-zA-Z0-9\.\-]/;e.test(b.val())?(c.show(),d.attr("disabled",!0)):(c.hide(),d.attr("disabled",!1))}function f(){var c=a("#"+b.prefix+"-bucket").val(),d=k.find('input[name="object-prefix"]'),e=d.val();""!==e&&(e="&prefix="+encodeURIComponent(e));var f=as3cf.aws_bucket_link+c+e;a("#"+b.prefix+"-view-bucket").attr("href",f)}function g(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf.nonces.get_url_preview};a.each(a("#tab-"+as3cf.tabs.defaultTab+" .as3cf-main-settings form").serializeArray(),function(c,d){var e=d.name,f=d.value;e=e.replace("[]",""),b[e]=void 0===b[e]?f:a.isArray(b[e])?b[e].concat(f):[b[e],f]}),b.action="as3cf-get-url-preview",a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){alert(as3cf.strings.get_url_preview_error+c)},success:function(b,c,d){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function h(a){as3cf.buckets.bucketSelectLock=!1}function i(){a("#as3cf-remove-local-file").is(":checked")&&a("#as3cf-serve-from-s3").is(":not(:checked)")?a("#as3cf-lost-files-notice").show():a("#as3cf-lost-files-notice").hide()}function j(){a("#as3cf-remove-local-file").is(":checked")?a("#as3cf-remove-local-notice").show():a("#as3cf-remove-local-notice").hide()}var k,l={},m=/[^a-z0-9.-]/,n=!1,o=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){c=as3cf.tabs.sanitizeHash(c),o.hide(),k=a("#tab-"+c),k.show(),a(".nav-tab").removeClass("nav-tab-active"),a('a.nav-tab[data-tab="'+c+'"]').addClass("nav-tab-active"),a(".aws-main").attr("data-tab",c),k.attr("data-prefix")&&(b.prefix=k.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show"),"support"===c&&as3cf.tabs.getDiagnosticInfo()},getDiagnosticInfo:function(){var b=a(".debug-log-textarea");b.html(as3cf.strings.get_diagnostic_info);var c={action:"as3cf-get-diagnostic-info",_nonce:as3cf.nonces.get_diagnostic_info};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:c,error:function(a,c,d){b.html(d)},success:function(a,c,d){"undefined"!=typeof a.success?b.html(a.diagnostic_info):(b.html(as3cf.strings.get_diagnostic_info_error),b.append(a.error))}})},sanitizeHash:function(b){var c=a("#tab-"+b);return 0===c.length&&(b=as3cf.tabs.defaultTab),b}},as3cf.buckets={validLength:3,bucketSelectLock:!1,loadList:function(c){"undefined"==typeof c&&(c=!1);var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-list"),e=a("#"+b.prefix+"-bucket").val();if(!1===c&&d.find("li").length>1)return a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"),void this.scrollToSelected();d.html('<li class="loading">'+d.attr("data-working")+"</li>");var f={action:b.prefix+"-get-buckets",_nonce:window[b.prefix.replace(/-/g,"_")].nonces.get_buckets},g=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:f,error:function(a,b,c){d.html(""),g.showError(as3cf.strings.get_buckets_error,c,"as3cf-bucket-select")},success:function(b,c,f){d.html(""),"undefined"!=typeof b.success?(a(".as3cf-bucket-error").hide(),a(b.buckets).each(function(a,b){var c=b.Name===e?"selected":"";d.append('<li><a class="'+c+'" href="#" data-bucket="'+b.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+b.Name+'</span><span class="spinner"></span></span></a></li>')}),g.scrollToSelected()):g.showError(as3cf.strings.get_buckets_error,b.error,"as3cf-bucket-select")}})},scrollToSelected:function(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a(".as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}},resetModal:function(){var c=a(".as3cf-bucket-container."+b.prefix);!1===k.hasClass("as3cf-has-bucket")||"manual"===a("#"+b.prefix+"-bucket-select").val()?(c.find(".as3cf-bucket-manual").show().siblings().hide(),c.find(".bucket-actions.manual").show().siblings(".bucket-actions").hide()):(c.find(".as3cf-bucket-select").show().siblings().hide(),c.find(".bucket-actions.select").show().siblings(".bucket-actions").hide(),this.loadList(n),n=!1),c.find(".as3cf-bucket-error").hide();var d=a("#"+b.prefix+"-bucket").val();c.find(".as3cf-bucket-manual .as3cf-bucket-name").val(d),this.bucketSelectLock=!1},saveManual:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find("button[type=submit]"),f=d.val(),g=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),k.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var h={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(a,b,c){e.text(g),i.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c,d,h){e.text(g),e.prop("disabled",!1),"undefined"!=typeof c.success?(i.set(f,c.region,c.can_write),a("#"+b.prefix+"-bucket-select").val("manual"),a(".as3cf-bucket-list a").removeClass("selected").filter('[data-bucket="'+f+'"]').addClass("selected"),n=!0):i.showError(as3cf.strings.save_bucket_error,c.error,"as3cf-bucket-manual")}})},saveSelected:function(c){var d=a(".as3cf-bucket-list");if(!this.bucketSelectLock){if(this.bucketSelectLock=!0,c.hasClass("selected"))return k.addClass("as3cf-has-bucket"),void b.close();var e=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),c.addClass("selected"),d.addClass("saving"),c.find(".spinner").show().css("visibility","visible");var f=c.attr("data-bucket"),g={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},h=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(b,c,f){d.removeClass("saving"),h.showError(as3cf.strings.save_bucket_error,f,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected")},success:function(g,i,j){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(h.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(h.showError(as3cf.strings.save_bucket_error,g.error,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"))}})}},disabledButtons:function(){if(0!==a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form").length){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").attr("disabled",!1),d.find(".as3cf-bucket-name").val().length<3?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").attr("disabled",!1)}},showError:function(b,c,d){var e=a(".as3cf-bucket-container").children(":visible"),f=e.find(".as3cf-bucket-error");d="undefined"==typeof d?null:d,d&&!e.hasClass(d)||(f.find("span.title").html(b+" &mdash;"),f.find("span.message").html(c),f.show(),this.bucketSelectLock=!1)},set:function(e,i,j){var m=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),n=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&0===n.text().trim().length){d("as3cf-copy-to-s3-wrap"),d("as3cf-serve-from-s3-wrap");var o=k.attr("id");l[o]=c(o)}a(".as3cf-error.fatal").hide(),n.text(e),m.find(".as3cf-bucket-name").val(e),a("#"+b.prefix+"-bucket").val(e),a("#"+b.prefix+"-region").val(i),a(".updated").not(".as3cf-notice").show(),k.addClass("as3cf-has-bucket"),k.find(".as3cf-can-write-error").toggle(!j),k.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&g(),f(),b.close(h)},create:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find(".bucket-create-region"),f=c.find("button[type=submit]"),g=d.val(),h=f.text();a(".as3cf-bucket-error").hide(),f.text(f.attr("data-working")),f.prop("disabled",!0);var i={action:b.prefix+"-create-bucket",bucket_name:g,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.create_bucket};e.val()&&(i.region=e.val());var j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){f.text(h),j.showError(as3cf.strings.create_bucket_error,c,"as3cf-bucket-create")},success:function(b,c,e){f.text(h),f.prop("disabled",!1),"undefined"!=typeof b.success?(j.set(g,b.region,b.can_write),a(".as3cf-bucket-select-region").hide(),a(".as3cf-bucket-select-region").removeAttr("selected"),d.val(""),f.attr("disabled",!0),n=!0):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return!(a.length<3||a.length>63)&&!0!==m.test(a)},updateNameNotice:function(b){var c=null;!0===m.test(b)?c=as3cf.strings.create_bucket_invalid_chars:b.length<3?c=as3cf.strings.create_bucket_name_short:b.length>63&&(c=as3cf.strings.create_bucket_name_long),c&&b.length>0?a(".as3cf-invalid-bucket-name").html(c):a(".as3cf-invalid-bucket-name").html("")}},a(document).ready(function(){var h=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(h),window.location.hash){var m=window.location.hash.substring(1);as3cf.tabs.toggle(m,!0)}else k=a("#tab-"+as3cf.tabs.defaultTab),a(".aws-main").attr("data-tab",as3cf.tabs.defaultTab);a(".aws-main").on("click",".nav-tab",function(b){if(b.preventDefault(),!a(this).hasClass("nav-tab-active")){var c=a(this).attr("data-tab");as3cf.tabs.toggle(c),"media"===c?(window.location.hash="","function"==typeof window.history.replaceState&&"#"===window.location.href.slice(-1)&&history.replaceState({},"",window.location.href.slice(0,-1))):window.location.hash=c}}),o.length&&o.each(function(a,b){l[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(l)){var b=k.attr("id");return c(b)!==l[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(b){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(b){a(this).hasClass("disabled")||d(a(this).attr("id"))}),o.on("change",".sub-toggle",function(b){var c=a(this).attr("id");a(".as3cf-setting."+c).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(b){var c=a(this).closest('input:radio[name="domain"]:checked'),d=c.val(),e=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),f="cloudfront"===d;e.toggleClass("hide",!f)}),a(".url-preview").on("change","input",function(a){g()}),i(),a("#as3cf-serve-from-s3,#as3cf-remove-local-file").on("change",function(a){i()}),j(),a("#as3cf-remove-local-file").on("change",function(a){j()}),a('.as3cf-setting input[type="text"]').keypress(function(a){if(13===a.which)return a.preventDefault(),!1}),a('input[name="cloudfront"]').on("keyup",function(b){e(a(this))}),a('input[name="domain"]').on("change",function(b){var c=a(this),d=a("#"+k.attr("id")+' form button[type="submit"]');"cloudfront"!==c.val()?d.attr("disabled",!1):e(c.next(".as3cf-setting").find('input[name="cloudfront"]'))}),a('input[name="object-prefix"]').on("change",function(a){f()}),a("#tab-media > .as3cf-bucket-error").detach().insertAfter(".as3cf-bucket-container h3"),a("body").on("click",".bucket-action-manual",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-manual").show().siblings().hide()}),a("body").on("click",".bucket-action-browse",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-select").show().siblings().hide(),as3cf.buckets.loadList(n),n=!1}),a("body").on("click",".bucket-action-create",function(c){c.preventDefault(),a(".as3cf-bucket-name").val(""),a(".as3cf-invalid-bucket-name").html(""),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-create").show().siblings().hide()}),a("body").on("click",".bucket-action-cancel",function(a){a.preventDefault(),as3cf.buckets.resetModal()}),a("body").on("click",".bucket-action-save",function(a){a.preventDefault(),as3cf.buckets.saveManual()}),a("body").on("click",'.as3cf-create-bucket-form button[type="submit"]',function(a){a.preventDefault(),as3cf.buckets.create()}),a("body").on("click",".bucket-action-refresh",function(a){a.preventDefault(),as3cf.buckets.loadList(!0)}),a("body").on("click",".as3cf-bucket-list a",function(b){b.preventDefault(),as3cf.buckets.saveSelected(a(this))}),a(".as3cf-bucket-container").on("click","a.js-link",function(b){return b.preventDefault(),window.open(a(this).attr("href")),!1}),a("body").on("as3cf-modal-open",function(c,d){if(".as3cf-bucket-container."+b.prefix===d){as3cf.buckets.resetModal();var e=a(".as3cf-bucket-manual h3").data("modal-title");a(".as3cf-bucket-manual h3").text(e),as3cf.buckets.disabledButtons()}}),as3cf.buckets.disabledButtons(),a("body").on("input keyup",".as3cf-create-bucket-form .as3cf-bucket-name",function(c){var d=a(this).val(),e=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(d)?e.find("button[type=submit]").removeAttr("disabled"):e.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(d)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(c){var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");d.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
1
+ !function(a,b){function c(b){return a("#"+b+" .as3cf-main-settings form").find("input:not(.no-compare)").serialize()}function d(a){var b=k.find("#"+a),c=b.find("input[type=checkbox]");b.toggleClass("on").find("span").toggleClass("checked");var d=b.find("span.on").hasClass("checked");c.attr("checked",d).trigger("change")}function e(b){var c=b.next(".as3cf-validation-error"),d=a("#"+k.attr("id")+' form button[type="submit"]'),e=/[^a-zA-Z0-9\.\-]/;e.test(b.val())?(c.show(),d.attr("disabled",!0)):(c.hide(),d.attr("disabled",!1))}function f(){var c=a("#"+b.prefix+"-bucket").val(),d=k.find('input[name="object-prefix"]'),e=d.val();""!==e&&(e="&prefix="+encodeURIComponent(e));var f=as3cf.aws_bucket_link+c+e;a("#"+b.prefix+"-view-bucket").attr("href",f)}function g(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf.nonces.get_url_preview};a.each(a("#tab-"+as3cf.tabs.defaultTab+" .as3cf-main-settings form").serializeArray(),function(c,d){var e=d.name,f=d.value;e=e.replace("[]",""),b[e]=void 0===b[e]?f:a.isArray(b[e])?b[e].concat(f):[b[e],f]}),b.action="as3cf-get-url-preview",a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){alert(as3cf.strings.get_url_preview_error+c)},success:function(b,c,d){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function h(){a("#as3cf-remove-local-file").is(":checked")&&a("#as3cf-serve-from-s3").is(":not(:checked)")?a("#as3cf-lost-files-notice").show():a("#as3cf-lost-files-notice").hide()}function i(){a("#as3cf-remove-local-file").is(":checked")?a("#as3cf-remove-local-notice").show():a("#as3cf-remove-local-notice").hide()}function j(){return"#"+as3cf.tabs.defaultTab===location.hash?void(location.hash=""):("function"==typeof history.replaceState&&"#"===location.href.slice(-1)&&history.replaceState({},"",location.href.slice(0,-1)),void as3cf.tabs.toggle(location.hash.replace("#",""),!0))}var k,l={},m=/[^a-z0-9.-]/,n=!1,o=a(".as3cf-tab");as3cf.tabs={defaultTab:"media",toggle:function(c,d){c=as3cf.tabs.sanitizeHash(c),o.hide(),k=a("#tab-"+c),k.show(),a(".nav-tab").removeClass("nav-tab-active"),a('a.nav-tab[data-tab="'+c+'"]').addClass("nav-tab-active"),a(".aws-main").attr("data-tab",c),k.attr("data-prefix")&&(b.prefix=k.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show"),"support"===c&&as3cf.tabs.getDiagnosticInfo()},getDiagnosticInfo:function(){var b=a(".debug-log-textarea");b.html(as3cf.strings.get_diagnostic_info);var c={action:"as3cf-get-diagnostic-info",_nonce:as3cf.nonces.get_diagnostic_info};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:c,error:function(a,c,d){b.html(d)},success:function(a,c,d){"undefined"!=typeof a.success?b.html(a.diagnostic_info):(b.html(as3cf.strings.get_diagnostic_info_error),b.append(a.error))}})},sanitizeHash:function(b){var c=a("#tab-"+b);return 0===c.length&&(b=as3cf.tabs.defaultTab),b}},as3cf.buckets={validLength:3,bucketSelectLock:!1,loadList:function(c){"undefined"==typeof c&&(c=!1);var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-list"),e=a("#"+b.prefix+"-bucket").val();if(!1===c&&d.find("li").length>1)return a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"),void this.scrollToSelected();d.html('<li class="loading">'+d.attr("data-working")+"</li>");var f={action:b.prefix+"-get-buckets",_nonce:window[b.prefix.replace(/-/g,"_")].nonces.get_buckets},g=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:f,error:function(a,b,c){d.html(""),g.showError(as3cf.strings.get_buckets_error,c,"as3cf-bucket-select")},success:function(b,c,f){d.html(""),"undefined"!=typeof b.success?(a(".as3cf-bucket-error").hide(),a(b.buckets).each(function(a,b){var c=b.Name===e?"selected":"";d.append('<li><a class="'+c+'" href="#" data-bucket="'+b.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+b.Name+'</span><span class="spinner"></span></span></a></li>')}),g.scrollToSelected()):g.showError(as3cf.strings.get_buckets_error,b.error,"as3cf-bucket-select")}})},scrollToSelected:function(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a(".as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}},resetModal:function(){var c=a(".as3cf-bucket-container."+b.prefix);!1===k.hasClass("as3cf-has-bucket")||"manual"===a("#"+b.prefix+"-bucket-select").val()?(c.find(".as3cf-bucket-manual").show().siblings().hide(),c.find(".bucket-actions.manual").show().siblings(".bucket-actions").hide()):(c.find(".as3cf-bucket-select").show().siblings().hide(),c.find(".bucket-actions.select").show().siblings(".bucket-actions").hide(),this.loadList(n),n=!1),c.find(".as3cf-bucket-error").hide();var d=a("#"+b.prefix+"-bucket").val();c.find(".as3cf-bucket-manual .as3cf-bucket-name").val(d),this.bucketSelectLock=!1},saveManual:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find("button[type=submit]"),f=d.val(),g=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),k.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var h={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket},i=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(a,b,c){e.text(g),i.showError(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c,d,h){e.text(g),e.prop("disabled",!1),"undefined"!=typeof c.success?(i.set(f,c.region,c.can_write),a("#"+b.prefix+"-bucket-select").val("manual"),a(".as3cf-bucket-list a").removeClass("selected").filter('[data-bucket="'+f+'"]').addClass("selected"),n=!0):i.showError(as3cf.strings.save_bucket_error,c.error,"as3cf-bucket-manual")}})},saveSelected:function(c){var d=a(".as3cf-bucket-list");if(!this.bucketSelectLock){if(this.bucketSelectLock=!0,c.hasClass("selected"))return k.addClass("as3cf-has-bucket"),void b.close();var e=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),c.addClass("selected"),d.addClass("saving"),c.find(".spinner").show().css("visibility","visible");var f=c.attr("data-bucket"),g={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket},h=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(b,c,f){d.removeClass("saving"),h.showError(as3cf.strings.save_bucket_error,f,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected")},success:function(g,i,j){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(h.set(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(h.showError(as3cf.strings.save_bucket_error,g.error,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"))}})}},disabledButtons:function(){if(0!==a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form").length){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").attr("disabled",!1),d.find(".as3cf-bucket-name").val().length<3?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").attr("disabled",!1)}},showError:function(b,c,d){var e=a(".as3cf-bucket-container").children(":visible"),f=e.find(".as3cf-bucket-error");d="undefined"==typeof d?null:d,d&&!e.hasClass(d)||(f.find("span.title").html(b+" &mdash;"),f.find("span.message").html(c),f.show(),this.bucketSelectLock=!1)},set:function(e,h,i){var j=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),m=a("#"+b.prefix+"-active-bucket");if("as3cf"===b.prefix&&0===m.text().trim().length){d("as3cf-copy-to-s3-wrap"),d("as3cf-serve-from-s3-wrap");var n=k.attr("id");l[n]=c(n)}a(".as3cf-error.fatal").hide(),m.text(e),j.find(".as3cf-bucket-name").val(e),a("#"+b.prefix+"-bucket").val(e),a("#"+b.prefix+"-region").val(h),a(".updated").not(".as3cf-notice").show(),k.addClass("as3cf-has-bucket"),k.find(".as3cf-can-write-error").toggle(!i),k.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&g(),f(),b.close(function(){k.trigger("bucket-change",[i]),as3cf.buckets.bucketSelectLock=!1})},create:function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find(".bucket-create-region"),f=c.find("button[type=submit]"),g=d.val(),h=f.text();a(".as3cf-bucket-error").hide(),f.text(f.attr("data-working")),f.prop("disabled",!0);var i={action:b.prefix+"-create-bucket",bucket_name:g,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.create_bucket};e.val()&&(i.region=e.val());var j=this;a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){f.text(h),j.showError(as3cf.strings.create_bucket_error,c,"as3cf-bucket-create")},success:function(b,c,e){f.text(h),f.prop("disabled",!1),"undefined"!=typeof b.success?(j.set(g,b.region,b.can_write),a(".as3cf-bucket-select-region").hide(),a(".as3cf-bucket-select-region").removeAttr("selected"),d.val(""),f.attr("disabled",!0),n=!0):j.showError(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})},isValidName:function(a){return!(a.length<3||a.length>63)&&!0!==m.test(a)},updateNameNotice:function(b){var c=null;!0===m.test(b)?c=as3cf.strings.create_bucket_invalid_chars:b.length<3?c=as3cf.strings.create_bucket_name_short:b.length>63&&(c=as3cf.strings.create_bucket_name_long),c&&b.length>0?a(".as3cf-invalid-bucket-name").html(c):a(".as3cf-invalid-bucket-name").html("")}},a(document).ready(function(){j(),window.onhashchange=j;var m=a(".wrap.aws-main .nav-tab-wrapper");a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(m),o.length&&o.each(function(a,b){l[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(l)){var b=k.attr("id");return c(b)!==l[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(b){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(b){a(this).hasClass("disabled")||d(a(this).attr("id"))}),o.on("change",".sub-toggle",function(b){var c=a(this).attr("id");a(".as3cf-setting."+c).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(b){var c=a(this).closest('input:radio[name="domain"]:checked'),d=c.val(),e=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),f="cloudfront"===d;e.toggleClass("hide",!f)}),a(".url-preview").on("change","input",function(a){g()}),h(),a("#as3cf-serve-from-s3,#as3cf-remove-local-file").on("change",function(a){h()}),i(),a("#as3cf-remove-local-file").on("change",function(a){i()}),a('.as3cf-setting input[type="text"]').keypress(function(a){if(13===a.which)return a.preventDefault(),!1}),a('input[name="cloudfront"]').on("keyup",function(b){e(a(this))}),a('input[name="domain"]').on("change",function(b){var c=a(this),d=a("#"+k.attr("id")+' form button[type="submit"]');"cloudfront"!==c.val()?d.attr("disabled",!1):e(c.next(".as3cf-setting").find('input[name="cloudfront"]'))}),a('input[name="object-prefix"]').on("change",function(a){f()}),a("#tab-media > .as3cf-bucket-error").detach().insertAfter(".as3cf-bucket-container h3"),a("body").on("click",".bucket-action-manual",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-manual").show().siblings().hide()}),a("body").on("click",".bucket-action-browse",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-select").show().siblings().hide(),as3cf.buckets.loadList(n),n=!1}),a("body").on("click",".bucket-action-create",function(c){c.preventDefault(),a(".as3cf-bucket-name").val(""),a(".as3cf-invalid-bucket-name").html(""),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-create").show().siblings().hide()}),a("body").on("click",".bucket-action-cancel",function(a){a.preventDefault(),as3cf.buckets.resetModal()}),a("body").on("click",".bucket-action-save",function(a){a.preventDefault(),as3cf.buckets.saveManual()}),a("body").on("click",'.as3cf-create-bucket-form button[type="submit"]',function(a){a.preventDefault(),as3cf.buckets.create()}),a("body").on("click",".bucket-action-refresh",function(a){a.preventDefault(),as3cf.buckets.loadList(!0)}),a("body").on("click",".as3cf-bucket-list a",function(b){b.preventDefault(),as3cf.buckets.saveSelected(a(this))}),a(".as3cf-bucket-container").on("click","a.js-link",function(b){return b.preventDefault(),window.open(a(this).attr("href")),!1}),a("body").on("as3cf-modal-open",function(c,d){if(".as3cf-bucket-container."+b.prefix===d){as3cf.buckets.resetModal();var e=a(".as3cf-bucket-manual h3").data("modal-title");a(".as3cf-bucket-manual h3").text(e),as3cf.buckets.disabledButtons()}}),as3cf.buckets.disabledButtons(),a("body").on("input keyup",".as3cf-create-bucket-form .as3cf-bucket-name",function(c){var d=a(this).val(),e=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");as3cf.buckets.isValidName(d)?e.find("button[type=submit]").removeAttr("disabled"):e.find("button[type=submit]").attr("disabled",!0),as3cf.buckets.updateNameNotice(d)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(c){var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");d.find(".as3cf-bucket-name").val().length<as3cf.buckets.validLength?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -1,5 +1,12 @@
1
  <?php
2
 
 
 
 
 
 
 
 
3
  class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4
 
5
  /**
@@ -115,12 +122,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
115
  $this->plugin_title = __( 'Offload S3 Lite', 'amazon-s3-and-cloudfront' );
116
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'amazon-s3-and-cloudfront' );
117
 
118
- new AS3CF_Upgrade_Region_Meta( $this );
119
- new AS3CF_Upgrade_File_Sizes( $this );
120
- new AS3CF_Upgrade_Meta_WP_Error( $this );
121
- new AS3CF_Upgrade_Content_Replace_URLs( $this );
122
- new AS3CF_Upgrade_EDD_Replace_URLs( $this );
123
- new AS3CF_Upgrade_Filter_Post_Excerpt( $this );
124
 
125
  // Plugin setup
126
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
@@ -150,7 +157,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
150
  add_filter( 'wp_prepare_attachment_for_js', array( $this, 'maybe_encode_wp_prepare_attachment_for_js', ), 99, 3 );
151
  add_filter( 'image_get_intermediate_size', array( $this, 'maybe_encode_image_get_intermediate_size' ), 99, 3 );
152
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
153
- add_filter( 'wp_audio_shortcode', array( $this, 'wp_audio_shortcode' ), 100, 5 );
 
154
 
155
  // Communication with S3, plugin needs to be setup
156
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
@@ -213,14 +221,14 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
213
  'key' => $key,
214
  'disabled' => false,
215
  'disabled_attr' => '',
216
- 'tr_class' => '',
217
  'setting_msg' => '',
218
  );
219
 
220
  if ( false !== $is_defined ) {
221
  $args['disabled'] = true;
222
  $args['disabled_attr'] = 'disabled="disabled"';
223
- $args['tr_class'] = 'as3cf-defined-setting';
224
  $args['setting_msg'] = '<span class="as3cf-defined-in-config">' . __( 'defined in wp-config.php', 'as3cf' ) . '</span>';
225
  }
226
 
@@ -385,8 +393,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
385
  }
386
 
387
  // Store the region for future use
388
- parent::set_setting( 'region', $region );
389
- $this->save_settings();
 
 
390
 
391
  return $region;
392
  }
@@ -779,7 +789,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
779
  $prefix = $this->normalize_object_prefix( $s3object['key'] );
780
  $bucket = $s3object['bucket'];
781
  $region = $this->get_s3object_region( $s3object );
782
- $paths = $this->get_attachment_file_paths( $post_id, false, false, $remove_backup_sizes );
783
  $paths = apply_filters( 'as3cf_remove_attachment_paths', $paths, $post_id, $s3object, $remove_backup_sizes );
784
 
785
  if ( is_wp_error( $region ) ) {
@@ -845,9 +855,13 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
845
  }
846
 
847
  // upload attachment to S3
848
- $data = $this->upload_attachment_to_s3( $post_id, $data );
849
 
850
- return $data;
 
 
 
 
851
  }
852
 
853
  /**
@@ -979,28 +993,27 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
979
  $args['ContentEncoding'] = 'gzip';
980
  }
981
 
982
- $args = apply_filters( 'as3cf_object_meta', $args, $post_id );
 
983
 
984
  do_action( 'as3cf_upload_attachment_pre_remove', $post_id, $s3object, $prefix, $args );
985
 
986
  $files_to_remove = array();
987
 
988
- if ( file_exists( $file_path ) ) {
989
- try {
990
- $s3client->putObject( $args );
991
- $files_to_remove[] = $file_path;
992
- } catch ( Exception $e ) {
993
- $error_msg = sprintf( __( 'Error uploading %s to S3: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
994
 
995
- return $this->return_upload_error( $error_msg, $return_metadata );
996
- }
997
  }
998
 
999
  delete_post_meta( $post_id, 'amazonS3_info' );
1000
 
1001
  add_post_meta( $post_id, 'amazonS3_info', $s3object );
1002
 
1003
- $file_paths = $this->get_attachment_file_paths( $post_id, true, $data );
1004
  $additional_images = array();
1005
 
1006
  $filesize_total = 0;
@@ -1028,7 +1041,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1028
  if ( ! in_array( $file_path, $files_to_remove ) ) {
1029
  $acl = apply_filters( 'as3cf_upload_acl_sizes', self::DEFAULT_ACL, $size, $post_id, $data );
1030
 
1031
- $additional_images[] = array(
1032
  'Key' => $prefix . wp_basename( $file_path ),
1033
  'SourceFile' => $file_path,
1034
  'ACL' => $acl,
@@ -1039,7 +1052,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1039
  $s3object_sizes[ $size ]['acl'] = $acl;
1040
  }
1041
 
1042
- if ( $remove_local_files_setting ) {
1043
  // Record the file size for the additional image
1044
  $bytes = filesize( $file_path );
1045
  if ( false !== $bytes ) {
@@ -1049,13 +1062,21 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1049
  }
1050
  }
1051
 
1052
- foreach ( $additional_images as $image ) {
 
 
 
 
 
 
 
 
 
1053
  try {
1054
- $args = array_merge( $args, $image );
1055
  $s3client->putObject( $args );
1056
  $files_to_remove[] = $image['SourceFile'];
1057
  } catch ( Exception $e ) {
1058
- AS3CF_Error::log( 'Error uploading ' . $args['SourceFile'] . ' to S3: ' . $e->getMessage() );
1059
  }
1060
  }
1061
 
@@ -1101,6 +1122,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1101
 
1102
  do_action( 'wpos3_post_upload_attachment', $post_id, $s3object );
1103
 
 
 
 
 
1104
  if ( ! is_null( $return_metadata ) ) {
1105
  // If the attachment metadata is supplied, return it
1106
  return $data;
@@ -1168,7 +1193,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1168
  }
1169
 
1170
  /**
1171
- * Helper to return meta data on upload error
1172
  *
1173
  * @param string $error_msg
1174
  * @param array|null $return
@@ -1176,12 +1201,13 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1176
  * @return array|WP_Error
1177
  */
1178
  protected function return_upload_error( $error_msg, $return = null ) {
 
 
 
1179
  if ( is_null( $return ) ) {
1180
  return new WP_Error( 'exception', $error_msg );
1181
  }
1182
 
1183
- AS3CF_Error::log( $error_msg );
1184
-
1185
  return $return;
1186
  }
1187
 
@@ -1429,8 +1455,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1429
  }
1430
 
1431
  $s3client = $this->get_s3client( $region );
1432
- $prefix = ltrim( trailingslashit( $this->get_object_prefix() ), '/' );
1433
- $prefix .= ltrim( trailingslashit( $this->get_dynamic_prefix( $time ) ), '/' );
1434
 
1435
  return $s3client->doesObjectExist( $bucket, $prefix . $filename );
1436
  }
@@ -1464,7 +1490,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1464
  * @return mixed
1465
  */
1466
  public function get_attachment_s3_info( $post_id ) {
1467
- return apply_filters( 'as3cf_get_attachment_s3_info', get_post_meta( $post_id, 'amazonS3_info', true ), $post_id );
 
 
 
 
 
 
 
 
1468
  }
1469
 
1470
  /**
@@ -1502,9 +1536,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1502
  * @param array $headers Header overrides for request
1503
  * @param bool $skip_rewrite_check
1504
  *
1505
- * @return mixed|void|WP_Error
1506
  */
1507
- function get_secure_attachment_url( $post_id, $expires = null, $size = null, $headers = array(), $skip_rewrite_check = false ) {
1508
  if ( is_null( $expires ) ) {
1509
  $expires = self::DEFAULT_EXPIRES;
1510
  }
@@ -1561,12 +1595,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1561
  */
1562
  function get_object_prefix( $toggle_setting = 'enable-object-prefix' ) {
1563
  if ( $this->get_setting( $toggle_setting ) ) {
1564
- $prefix = trim( $this->get_setting( 'object-prefix' ) );
1565
- } else {
1566
- $prefix = '';
1567
  }
1568
 
1569
- return $prefix;
1570
  }
1571
 
1572
  /**
@@ -1576,12 +1608,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1576
  *
1577
  * @return string
1578
  */
1579
- function get_file_prefix( $time = null ) {
1580
- $prefix = ltrim( trailingslashit( $this->get_object_prefix() ), '/' );
1581
- $prefix .= ltrim( trailingslashit( $this->get_dynamic_prefix( $time ) ), '/' );
1582
 
1583
  if ( $this->get_setting( 'object-versioning' ) ) {
1584
- $prefix .= $this->get_object_version_string();
1585
  }
1586
 
1587
  return $prefix;
@@ -2423,7 +2455,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2423
  *
2424
  * @return Aws\S3\S3Client
2425
  */
2426
- function get_s3client( $region = false, $force = false ) {
2427
  if ( is_null( $this->s3client ) || $force ) {
2428
 
2429
  $args = array(
@@ -2577,34 +2609,30 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2577
  }
2578
  }
2579
 
 
 
 
 
 
 
 
 
 
2580
  if ( isset( self::$buckets_check[ $bucket ] ) ) {
2581
  return self::$buckets_check[ $bucket ];
2582
  }
2583
 
2584
- $file_name = 'as3cf-permission-check.txt';
2585
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'amazon-s3-and-cloudfront' );
2586
 
2587
- $path = $this->get_object_prefix();
2588
- $key = $path . $file_name;
2589
-
2590
- $args = array(
2591
- 'Bucket' => $bucket,
2592
- 'Key' => $key,
2593
- 'Body' => $file_contents,
2594
- 'ACL' => 'public-read',
2595
- );
2596
-
2597
  try {
2598
- // need to set region for buckets in non default region
2599
- if ( is_null( $region ) ) {
2600
- $region = $this->get_setting( 'region' );
2601
-
2602
- if ( is_wp_error( $region ) ) {
2603
- return $region;
2604
- }
2605
- }
2606
  // attempt to create the test file
2607
- $this->get_s3client( $region, true )->putObject( $args );
 
 
 
 
 
2608
  // delete it straight away if created
2609
  $this->get_s3client()->deleteObject( array(
2610
  'Bucket' => $bucket,
@@ -2657,15 +2685,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
2657
  wp_register_script( 'as3cf-modal', $src, array( 'jquery' ), $version, true );
2658
  }
2659
 
2660
- function plugin_load() {
2661
- $version = $this->get_asset_version();
2662
- $suffix = $this->get_asset_suffix();
2663
-
2664
- $src = plugins_url( 'assets/css/styles.css', $this->plugin_file_path );
2665
- wp_enqueue_style( 'as3cf-styles', $src, array( 'as3cf-modal' ), $version );
2666
-
2667
- $src = plugins_url( 'assets/js/script' . $suffix . '.js', $this->plugin_file_path );
2668
- wp_enqueue_script( 'as3cf-script', $src, array( 'jquery', 'as3cf-modal' ), $version, true );
2669
 
2670
  wp_localize_script( 'as3cf-script',
2671
  'as3cf',
@@ -3029,7 +3054,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3029
  *
3030
  * @return array|bool|WP_Error
3031
  */
3032
- function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) {
3033
  // Return early if already set to the desired ACL
3034
  if ( isset( $s3object['acl'] ) && $acl === $s3object['acl'] ) {
3035
  return false;
@@ -3689,80 +3714,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3689
  return $table_prefixes;
3690
  }
3691
 
3692
- /**
3693
- * Get file paths for all attachment versions.
3694
- *
3695
- * @param int $attachment_id
3696
- * @param bool $exists_locally
3697
- * @param array|bool $meta
3698
- * @param bool $include_backups
3699
- *
3700
- * @return array
3701
- */
3702
- public function get_attachment_file_paths( $attachment_id, $exists_locally = true, $meta = false, $include_backups = true ) {
3703
- $file_path = get_attached_file( $attachment_id, true );
3704
- $paths = array(
3705
- 'original' => $file_path,
3706
- );
3707
-
3708
- if ( ! $meta ) {
3709
- $meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
3710
- }
3711
-
3712
- if ( is_wp_error( $meta ) ) {
3713
- return $paths;
3714
- }
3715
-
3716
- $file_name = wp_basename( $file_path );
3717
-
3718
- // If file edited, current file name might be different.
3719
- if ( isset( $meta['file'] ) ) {
3720
- $paths['file'] = str_replace( $file_name, wp_basename( $meta['file'] ), $file_path );
3721
- }
3722
-
3723
- // Thumb
3724
- if ( isset( $meta['thumb'] ) ) {
3725
- $paths['thumb'] = str_replace( $file_name, $meta['thumb'], $file_path );
3726
- }
3727
-
3728
- // Sizes
3729
- if ( isset( $meta['sizes'] ) ) {
3730
- foreach ( $meta['sizes'] as $size => $file ) {
3731
- if ( isset( $file['file'] ) ) {
3732
- $paths[ $size ] = str_replace( $file_name, $file['file'], $file_path );
3733
- }
3734
- }
3735
- }
3736
-
3737
- $backups = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
3738
-
3739
- // Backups
3740
- if ( $include_backups && is_array( $backups ) ) {
3741
- foreach ( $backups as $size => $file ) {
3742
- if ( isset( $file['file'] ) ) {
3743
- $paths[ $size ] = str_replace( $file_name, $file['file'], $file_path );
3744
- }
3745
- }
3746
- }
3747
-
3748
- // Allow other processes to add files to be uploaded
3749
- $paths = apply_filters( 'as3cf_attachment_file_paths', $paths, $attachment_id, $meta );
3750
-
3751
- // Remove duplicates
3752
- $paths = array_unique( $paths );
3753
-
3754
- // Remove paths that don't exist
3755
- if ( $exists_locally ) {
3756
- foreach ( $paths as $key => $path ) {
3757
- if ( ! file_exists( $path ) ) {
3758
- unset( $paths[ $key ] );
3759
- }
3760
- }
3761
- }
3762
-
3763
- return $paths;
3764
- }
3765
-
3766
  /**
3767
  * Get the access denied bucket error notice message
3768
  *
@@ -3771,12 +3722,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3771
  * @return string
3772
  */
3773
  function get_access_denied_notice_message( $single = true ) {
3774
- $quick_start = sprintf( '<a class="js-link" href="%s">%s</a>', 'https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/#bucket-restrictions', __( 'Quick Start Guide', 'amazon-s3-and-cloudfront' ) );
 
 
 
 
3775
 
3776
  $message = sprintf( __( "Looks like we don't have write access to this bucket. It's likely that the user you've provided access keys for hasn't been granted the correct permissions. Please see our %s for instructions on setting up permissions correctly.", 'amazon-s3-and-cloudfront' ), $quick_start );
3777
  if ( ! $single ) {
3778
  $message = sprintf( __( "Looks like we don't have access to the buckets. It's likely that the user you've provided access keys for hasn't been granted the correct permissions. Please see our %s for instructions on setting up permissions correctly.", 'amazon-s3-and-cloudfront' ), $quick_start );
3779
-
3780
  }
3781
 
3782
  return $message;
@@ -3914,7 +3868,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3914
  's3' => $all_media_s3,
3915
  );
3916
 
3917
- set_site_transient( 'wpos3_attachment_counts', $attachment_counts, 2 * HOUR_IN_SECONDS );
3918
  }
3919
 
3920
  return $attachment_counts;
@@ -3955,69 +3909,49 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
3955
  }
3956
 
3957
  /**
3958
- * Create a More Info campaign url for given url.
3959
- *
3960
- * @param string $url
3961
  *
3962
  * @return string
3963
  */
3964
- private function more_info_campaign_url( $url ) {
3965
- $campaign = $this->is_pro() ? 'os3-pro-plugin' : 'os3-free-plugin';
3966
- $url .= '?utm_source=insideplugin&utm_medium=web&utm_content=more-info&utm_campaign=' . $campaign;
3967
-
3968
- return $url;
3969
  }
3970
 
3971
  /**
3972
- * Create a site link for given url, link text and optional anchor, usually with campaign.
3973
- *
3974
- * TODO: Update *all* hardcoded https://deliciousbrains.com urls to use relative path
3975
- * that this function then prepends with configured base URL.
3976
- * https://github.com/deliciousbrains/wp-aws/issues/1291
3977
  *
3978
- * @param string $url
3979
- * @param string $text
3980
- * @param string $hash Optional anchor text.
3981
- * @param bool $append_campaign
3982
  *
3983
  * @return string
3984
  */
3985
- public function dbrains_link( $url, $text, $hash = '', $append_campaign = true ) {
3986
- if ( $append_campaign ) {
3987
- $url = $this->more_info_campaign_url( $url );
3988
- }
3989
 
3990
- if ( ! empty( $hash ) ) {
3991
- $url .= '#' . $hash;
3992
  }
3993
 
3994
- return sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html( $text ) );
3995
- }
3996
-
3997
- /**
3998
- * More info link
3999
- *
4000
- * @param string $url
4001
- * @param string $hash
4002
- * @param bool $append_campaign
4003
- *
4004
- * @return string
4005
- */
4006
- public function more_info_link( $url, $hash = '', $append_campaign = true ) {
4007
- $link = $this->dbrains_link( $url, __( 'More info', 'amazon-s3-and-cloudfront' ), $hash, $append_campaign );
4008
 
4009
  return sprintf( '<span class="more-info">%s &raquo;</span>', $link );
4010
  }
4011
 
4012
  /**
4013
- * Settings more info link
4014
  *
4015
  * @param string $hash
 
4016
  *
4017
  * @return string
4018
  */
4019
- public function settings_more_info_link( $hash ) {
4020
- return $this->more_info_link( 'https://deliciousbrains.com/wp-offload-s3/doc/settings/', $hash );
4021
  }
4022
 
4023
  /**
@@ -4094,8 +4028,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4094
  'flash' => false,
4095
  );
4096
 
4097
- $doc_url = 'https://deliciousbrains.com/wp-offload-s3/doc/force-http-setting/';
4098
- $doc_link = $this->dbrains_link( $doc_url, __( 'this doc' ) );
 
 
4099
 
4100
  $message = sprintf( '<strong>%s</strong> &mdash; ', __( 'WP Offload S3 Feature Removed', 'amazon-s3-and-cloudfront' ) );
4101
  $message .= sprintf( __( 'You had the "Always non-SSL" option selected in your settings, but we\'ve removed this option in version 1.3. We\'ll now use HTTPS when the request is HTTPS and regular HTTP when the request is HTTP. This should work fine for your site, but please take a poke around and make sure things are working ok. See %s for more details on why we did this and how you can revert back to the old behavior.', 'amazon-s3-and-cloudfront' ), $doc_link );
@@ -4245,31 +4181,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4245
  * Load media assets.
4246
  */
4247
  public function load_media_assets() {
4248
- $version = $this->get_asset_version();
4249
- $suffix = $this->get_asset_suffix();
4250
-
4251
- $src = plugins_url( 'assets/css/media.css', $this->plugin_file_path );
4252
- wp_enqueue_style( 'as3cf-media-styles', $src, array( 'as3cf-modal' ), $version );
4253
-
4254
- $src = plugins_url( 'assets/js/media' . $suffix . '.js', $this->plugin_file_path );
4255
- wp_enqueue_script(
4256
- 'as3cf-media-script',
4257
- $src,
4258
- array( 'jquery', 'media-views', 'media-grid', 'wp-util' ),
4259
- $version,
4260
- true
4261
- );
4262
 
4263
- wp_localize_script(
4264
- 'as3cf-media-script',
4265
- 'as3cf_media',
4266
- array(
4267
  'strings' => $this->get_media_action_strings(),
4268
  'nonces' => array(
4269
  'get_attachment_s3_details' => wp_create_nonce( 'get-attachment-s3-details' ),
4270
  ),
4271
- )
4272
- );
4273
  }
4274
 
4275
  /**
@@ -4313,18 +4238,14 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4313
  * @param $hook_suffix
4314
  */
4315
  public function load_attachment_assets( $hook_suffix ) {
4316
- $version = $this->get_asset_version();
4317
- $suffix = $this->get_asset_suffix();
4318
-
4319
  global $post;
4320
- if ( 'post.php' != $hook_suffix || 'attachment' != $post->post_type ) {
4321
  return;
4322
  }
4323
 
4324
- $src = plugins_url( 'assets/css/attachment.css', $this->plugin_file_path );
4325
- wp_enqueue_style( 'as3cf-pro-attachment-styles', $src, array( 'as3cf-modal' ), $version );
4326
 
4327
- do_action( 'as3cf_load_attachment_assets', $version, $suffix );
4328
  }
4329
 
4330
  /**
@@ -4353,36 +4274,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4353
  return ( '.' === $directory ) ? '' : $directory . '/';
4354
  }
4355
 
4356
- /**
4357
- * Remove scheme from URL.
4358
- *
4359
- * @param string $url
4360
- *
4361
- * @return string
4362
- */
4363
- public function remove_scheme( $url ) {
4364
- return preg_replace( '/^(?:http|https):/', '', $url );
4365
- }
4366
-
4367
- /**
4368
- * Remove size from filename (image[-100x100].jpeg).
4369
- *
4370
- * @param string $url
4371
- * @param bool $remove_extension
4372
- *
4373
- * @return string
4374
- */
4375
- public function remove_size_from_filename( $url, $remove_extension = false ) {
4376
- $url = preg_replace( '/^(\S+)-[0-9]{1,4}x[0-9]{1,4}(\.[a-zA-Z0-9\.]{2,})?/', '$1$2', $url );
4377
-
4378
- if ( $remove_extension ) {
4379
- $ext = pathinfo( $url, PATHINFO_EXTENSION );
4380
- $url = str_replace( ".$ext", '', $url );
4381
- }
4382
-
4383
- return $url;
4384
- }
4385
-
4386
  /**
4387
  * Has the given attachment been uploaded by this instance?
4388
  *
@@ -4399,19 +4290,19 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4399
  }
4400
 
4401
  /**
4402
- * Filters the audio shortcode output to remove "&_=NN" params from source.src as it breaks signed URLs.
4403
  *
4404
- * @param string $html Audio shortcode HTML output.
4405
- * @param array $atts Array of audio shortcode attributes.
4406
- * @param string $audio Audio file.
4407
  * @param int $post_id Post ID.
4408
- * @param string $library Media library used for the audio shortcode.
4409
  *
4410
  * @return string
4411
  *
4412
  * Note: Depends on 30377.4.diff from https://core.trac.wordpress.org/ticket/30377
4413
  */
4414
- public function wp_audio_shortcode( $html, $atts, $audio, $post_id, $library ) {
4415
  $html = preg_replace( '/&#038;_=[0-9]+/', '', $html );
4416
 
4417
  return $html;
@@ -4435,4 +4326,45 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4435
 
4436
  return $url;
4437
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4438
  }
1
  <?php
2
 
3
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_Content_Replace_URLs;
4
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_EDD_Replace_URLs;
5
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_File_Sizes;
6
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_Filter_Post_Excerpt;
7
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_Meta_WP_Error;
8
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Upgrade_Region_Meta;
9
+
10
  class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
11
 
12
  /**
122
  $this->plugin_title = __( 'Offload S3 Lite', 'amazon-s3-and-cloudfront' );
123
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'amazon-s3-and-cloudfront' );
124
 
125
+ new Upgrade_Region_Meta( $this );
126
+ new Upgrade_File_Sizes( $this );
127
+ new Upgrade_Meta_WP_Error( $this );
128
+ new Upgrade_Content_Replace_URLs( $this );
129
+ new Upgrade_EDD_Replace_URLs( $this );
130
+ new Upgrade_Filter_Post_Excerpt( $this );
131
 
132
  // Plugin setup
133
  add_action( 'aws_admin_menu', array( $this, 'admin_menu' ) );
157
  add_filter( 'wp_prepare_attachment_for_js', array( $this, 'maybe_encode_wp_prepare_attachment_for_js', ), 99, 3 );
158
  add_filter( 'image_get_intermediate_size', array( $this, 'maybe_encode_image_get_intermediate_size' ), 99, 3 );
159
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
160
+ add_filter( 'wp_audio_shortcode', array( $this, 'wp_media_shortcode' ), 100, 5 );
161
+ add_filter( 'wp_video_shortcode', array( $this, 'wp_media_shortcode' ), 100, 5 );
162
 
163
  // Communication with S3, plugin needs to be setup
164
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
221
  'key' => $key,
222
  'disabled' => false,
223
  'disabled_attr' => '',
224
+ 'tr_class' => str_replace( '_', '-', $this->plugin_prefix . '-' . $key . '-container' ),
225
  'setting_msg' => '',
226
  );
227
 
228
  if ( false !== $is_defined ) {
229
  $args['disabled'] = true;
230
  $args['disabled_attr'] = 'disabled="disabled"';
231
+ $args['tr_class'] .= ' as3cf-defined-setting';
232
  $args['setting_msg'] = '<span class="as3cf-defined-in-config">' . __( 'defined in wp-config.php', 'as3cf' ) . '</span>';
233
  }
234
 
393
  }
394
 
395
  // Store the region for future use
396
+ if ( is_string( $region ) ) {
397
+ parent::set_setting( 'region', $region );
398
+ $this->save_settings();
399
+ }
400
 
401
  return $region;
402
  }
789
  $prefix = $this->normalize_object_prefix( $s3object['key'] );
790
  $bucket = $s3object['bucket'];
791
  $region = $this->get_s3object_region( $s3object );
792
+ $paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, false, $remove_backup_sizes );
793
  $paths = apply_filters( 'as3cf_remove_attachment_paths', $paths, $post_id, $s3object, $remove_backup_sizes );
794
 
795
  if ( is_wp_error( $region ) ) {
855
  }
856
 
857
  // upload attachment to S3
858
+ $s3_meta = $this->upload_attachment_to_s3( $post_id, $data );
859
 
860
+ if ( is_wp_error( $s3_meta ) ) {
861
+ return $data;
862
+ }
863
+
864
+ return $s3_meta;
865
  }
866
 
867
  /**
993
  $args['ContentEncoding'] = 'gzip';
994
  }
995
 
996
+ $image_size = wp_attachment_is_image( $post_id ) ? 'full' : '';
997
+ $args = apply_filters( 'as3cf_object_meta', $args, $post_id, $image_size, false );
998
 
999
  do_action( 'as3cf_upload_attachment_pre_remove', $post_id, $s3object, $prefix, $args );
1000
 
1001
  $files_to_remove = array();
1002
 
1003
+ try {
1004
+ $s3client->putObject( $args );
1005
+ $files_to_remove[] = $file_path;
1006
+ } catch ( Exception $e ) {
1007
+ $error_msg = sprintf( __( 'Error uploading %s to S3: %s', 'amazon-s3-and-cloudfront' ), $file_path, $e->getMessage() );
 
1008
 
1009
+ return $this->return_upload_error( $error_msg, $return_metadata );
 
1010
  }
1011
 
1012
  delete_post_meta( $post_id, 'amazonS3_info' );
1013
 
1014
  add_post_meta( $post_id, 'amazonS3_info', $s3object );
1015
 
1016
+ $file_paths = AS3CF_Utils::get_attachment_file_paths( $post_id, false, $data );
1017
  $additional_images = array();
1018
 
1019
  $filesize_total = 0;
1041
  if ( ! in_array( $file_path, $files_to_remove ) ) {
1042
  $acl = apply_filters( 'as3cf_upload_acl_sizes', self::DEFAULT_ACL, $size, $post_id, $data );
1043
 
1044
+ $additional_images[ $size ] = array(
1045
  'Key' => $prefix . wp_basename( $file_path ),
1046
  'SourceFile' => $file_path,
1047
  'ACL' => $acl,
1052
  $s3object_sizes[ $size ]['acl'] = $acl;
1053
  }
1054
 
1055
+ if ( $remove_local_files_setting && file_exists( $file_path ) ) {
1056
  // Record the file size for the additional image
1057
  $bytes = filesize( $file_path );
1058
  if ( false !== $bytes ) {
1062
  }
1063
  }
1064
 
1065
+ $upload_errors = array();
1066
+
1067
+ foreach ( $additional_images as $size => $image ) {
1068
+ $args = apply_filters( 'as3cf_object_meta', array_merge( $args, $image ), $post_id, $size, false );
1069
+
1070
+ if ( ! file_exists( $args['SourceFile'] ) ) {
1071
+ $upload_errors[] = $this->return_upload_error( sprintf( __( 'File %s does not exist', 'amazon-s3-and-cloudfront' ), $args['SourceFile'] ) );
1072
+ continue;
1073
+ }
1074
+
1075
  try {
 
1076
  $s3client->putObject( $args );
1077
  $files_to_remove[] = $image['SourceFile'];
1078
  } catch ( Exception $e ) {
1079
+ $upload_errors[] = $this->return_upload_error( sprintf( __( 'Error uploading %s to S3: %s', 'amazon-s3-and-cloudfront' ), $args['SourceFile'], $e->getMessage() ) );
1080
  }
1081
  }
1082
 
1122
 
1123
  do_action( 'wpos3_post_upload_attachment', $post_id, $s3object );
1124
 
1125
+ if ( $upload_errors ) {
1126
+ return $this->consolidate_upload_errors( $upload_errors );
1127
+ }
1128
+
1129
  if ( ! is_null( $return_metadata ) ) {
1130
  // If the attachment metadata is supplied, return it
1131
  return $data;
1193
  }
1194
 
1195
  /**
1196
+ * Helper to record errors and return meta data on upload error.
1197
  *
1198
  * @param string $error_msg
1199
  * @param array|null $return
1201
  * @return array|WP_Error
1202
  */
1203
  protected function return_upload_error( $error_msg, $return = null ) {
1204
+
1205
+ AS3CF_Error::log( $error_msg );
1206
+
1207
  if ( is_null( $return ) ) {
1208
  return new WP_Error( 'exception', $error_msg );
1209
  }
1210
 
 
 
1211
  return $return;
1212
  }
1213
 
1455
  }
1456
 
1457
  $s3client = $this->get_s3client( $region );
1458
+ $prefix = AS3CF_Utils::trailingslash_prefix( $this->get_object_prefix() );
1459
+ $prefix .= AS3CF_Utils::trailingslash_prefix( $this->get_dynamic_prefix( $time ) );
1460
 
1461
  return $s3client->doesObjectExist( $bucket, $prefix . $filename );
1462
  }
1490
  * @return mixed
1491
  */
1492
  public function get_attachment_s3_info( $post_id ) {
1493
+ $s3_object = get_post_meta( $post_id, 'amazonS3_info', true );
1494
+
1495
+ if ( is_array( $s3_object ) ) {
1496
+ $s3_object = array_merge( array(
1497
+ 'region' => null,
1498
+ ), $s3_object );
1499
+ }
1500
+
1501
+ return apply_filters( 'as3cf_get_attachment_s3_info', $s3_object, $post_id );
1502
  }
1503
 
1504
  /**
1536
  * @param array $headers Header overrides for request
1537
  * @param bool $skip_rewrite_check
1538
  *
1539
+ * @return mixed|WP_Error
1540
  */
1541
+ public function get_secure_attachment_url( $post_id, $expires = null, $size = null, $headers = array(), $skip_rewrite_check = false ) {
1542
  if ( is_null( $expires ) ) {
1543
  $expires = self::DEFAULT_EXPIRES;
1544
  }
1595
  */
1596
  function get_object_prefix( $toggle_setting = 'enable-object-prefix' ) {
1597
  if ( $this->get_setting( $toggle_setting ) ) {
1598
+ return trailingslashit( trim( $this->get_setting( 'object-prefix' ) ) );
 
 
1599
  }
1600
 
1601
+ return '';
1602
  }
1603
 
1604
  /**
1608
  *
1609
  * @return string
1610
  */
1611
+ public function get_file_prefix( $time = null ) {
1612
+ $prefix = AS3CF_Utils::trailingslash_prefix( $this->get_object_prefix() );
1613
+ $prefix .= AS3CF_Utils::trailingslash_prefix( $this->get_dynamic_prefix( $time ) );
1614
 
1615
  if ( $this->get_setting( 'object-versioning' ) ) {
1616
+ $prefix .= AS3CF_Utils::trailingslash_prefix( $this->get_object_version_string() );
1617
  }
1618
 
1619
  return $prefix;
2455
  *
2456
  * @return Aws\S3\S3Client
2457
  */
2458
+ public function get_s3client( $region = false, $force = false ) {
2459
  if ( is_null( $this->s3client ) || $force ) {
2460
 
2461
  $args = array(
2609
  }
2610
  }
2611
 
2612
+ // need to set region for buckets in non default region
2613
+ if ( is_null( $region ) ) {
2614
+ $region = $this->get_setting( 'region' );
2615
+
2616
+ if ( is_wp_error( $region ) ) {
2617
+ return $region;
2618
+ }
2619
+ }
2620
+
2621
  if ( isset( self::$buckets_check[ $bucket ] ) ) {
2622
  return self::$buckets_check[ $bucket ];
2623
  }
2624
 
2625
+ $key = $this->get_file_prefix() . 'as3cf-permission-check.txt';
2626
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'amazon-s3-and-cloudfront' );
2627
 
 
 
 
 
 
 
 
 
 
 
2628
  try {
 
 
 
 
 
 
 
 
2629
  // attempt to create the test file
2630
+ $this->get_s3client( $region, true )->putObject( array(
2631
+ 'Bucket' => $bucket,
2632
+ 'Key' => $key,
2633
+ 'Body' => $file_contents,
2634
+ 'ACL' => 'public-read',
2635
+ ) );
2636
  // delete it straight away if created
2637
  $this->get_s3client()->deleteObject( array(
2638
  'Bucket' => $bucket,
2685
  wp_register_script( 'as3cf-modal', $src, array( 'jquery' ), $version, true );
2686
  }
2687
 
2688
+ /**
2689
+ * On plugin load.
2690
+ */
2691
+ public function plugin_load() {
2692
+ $this->enqueue_style( 'as3cf-styles', 'assets/css/styles', array( 'as3cf-modal' ) );
2693
+ $this->enqueue_script( 'as3cf-script', 'assets/js/script', array( 'jquery', 'as3cf-modal' ) );
 
 
 
2694
 
2695
  wp_localize_script( 'as3cf-script',
2696
  'as3cf',
3054
  *
3055
  * @return array|bool|WP_Error
3056
  */
3057
+ public function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) {
3058
  // Return early if already set to the desired ACL
3059
  if ( isset( $s3object['acl'] ) && $acl === $s3object['acl'] ) {
3060
  return false;
3714
  return $table_prefixes;
3715
  }
3716
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3717
  /**
3718
  * Get the access denied bucket error notice message
3719
  *
3722
  * @return string
3723
  */
3724
  function get_access_denied_notice_message( $single = true ) {
3725
+ $url = $this->dbrains_url( '/wp-offload-s3/doc/quick-start-guide/', array(
3726
+ 'utm_campaign' => 'error+messages',
3727
+ ), 'bucket-restrictions' );
3728
+
3729
+ $quick_start = sprintf( '<a class="js-link" href="%s">%s</a>', $url, __( 'Quick Start Guide', 'amazon-s3-and-cloudfront' ) );
3730
 
3731
  $message = sprintf( __( "Looks like we don't have write access to this bucket. It's likely that the user you've provided access keys for hasn't been granted the correct permissions. Please see our %s for instructions on setting up permissions correctly.", 'amazon-s3-and-cloudfront' ), $quick_start );
3732
  if ( ! $single ) {
3733
  $message = sprintf( __( "Looks like we don't have access to the buckets. It's likely that the user you've provided access keys for hasn't been granted the correct permissions. Please see our %s for instructions on setting up permissions correctly.", 'amazon-s3-and-cloudfront' ), $quick_start );
 
3734
  }
3735
 
3736
  return $message;
3868
  's3' => $all_media_s3,
3869
  );
3870
 
3871
+ set_site_transient( 'wpos3_attachment_counts', $attachment_counts, 2 * MINUTE_IN_SECONDS );
3872
  }
3873
 
3874
  return $attachment_counts;
3909
  }
3910
 
3911
  /**
3912
+ * Get UTM source for plugin.
 
 
3913
  *
3914
  * @return string
3915
  */
3916
+ protected function get_utm_source() {
3917
+ return $this->is_pro() ? 'OS3+Paid' : 'OS3+Free';
 
 
 
3918
  }
3919
 
3920
  /**
3921
+ * More info link.
 
 
 
 
3922
  *
3923
+ * @param string $path
3924
+ * @param string $utm_content
3925
+ * @param string $hash
 
3926
  *
3927
  * @return string
3928
  */
3929
+ public function more_info_link( $path, $utm_content = '', $hash = '' ) {
3930
+ $args = array(
3931
+ 'utm_campaign' => 'support+docs',
3932
+ );
3933
 
3934
+ if ( ! empty( $utm_content ) ) {
3935
+ $args['utm_content'] = $utm_content;
3936
  }
3937
 
3938
+ $url = $this->dbrains_url( $path, $args, $hash );
3939
+ $text = __( 'More info', 'amazon-s3-and-cloudfront' );
3940
+ $link = AS3CF_Utils::dbrains_link( $url, $text );
 
 
 
 
 
 
 
 
 
 
 
3941
 
3942
  return sprintf( '<span class="more-info">%s &raquo;</span>', $link );
3943
  }
3944
 
3945
  /**
3946
+ * Settings more info link.
3947
  *
3948
  * @param string $hash
3949
+ * @param string $utm_content
3950
  *
3951
  * @return string
3952
  */
3953
+ public function settings_more_info_link( $hash, $utm_content = '' ) {
3954
+ return $this->more_info_link( '/wp-offload-s3/doc/settings/', $utm_content, $hash );
3955
  }
3956
 
3957
  /**
4028
  'flash' => false,
4029
  );
4030
 
4031
+ $doc_url = $this->dbrains_url( '/wp-offload-s3/doc/force-http-setting/', array(
4032
+ 'utm_campaign' => 'support+docs'
4033
+ ) );
4034
+ $doc_link = AS3CF_Utils::dbrains_link( $doc_url, __( 'this doc' ) );
4035
 
4036
  $message = sprintf( '<strong>%s</strong> &mdash; ', __( 'WP Offload S3 Feature Removed', 'amazon-s3-and-cloudfront' ) );
4037
  $message .= sprintf( __( 'You had the "Always non-SSL" option selected in your settings, but we\'ve removed this option in version 1.3. We\'ll now use HTTPS when the request is HTTPS and regular HTTP when the request is HTTP. This should work fine for your site, but please take a poke around and make sure things are working ok. See %s for more details on why we did this and how you can revert back to the old behavior.', 'amazon-s3-and-cloudfront' ), $doc_link );
4181
  * Load media assets.
4182
  */
4183
  public function load_media_assets() {
4184
+ $this->enqueue_style( 'as3cf-media-styles', 'assets/css/media', array( 'as3cf-modal' ) );
4185
+ $this->enqueue_script( 'as3cf-media-script', 'assets/js/media', array(
4186
+ 'jquery',
4187
+ 'media-views',
4188
+ 'media-grid',
4189
+ 'wp-util',
4190
+ ) );
 
 
 
 
 
 
 
4191
 
4192
+ wp_localize_script( 'as3cf-media-script', 'as3cf_media', array(
 
 
 
4193
  'strings' => $this->get_media_action_strings(),
4194
  'nonces' => array(
4195
  'get_attachment_s3_details' => wp_create_nonce( 'get-attachment-s3-details' ),
4196
  ),
4197
+ ) );
 
4198
  }
4199
 
4200
  /**
4238
  * @param $hook_suffix
4239
  */
4240
  public function load_attachment_assets( $hook_suffix ) {
 
 
 
4241
  global $post;
4242
+ if ( 'post.php' !== $hook_suffix || 'attachment' !== $post->post_type ) {
4243
  return;
4244
  }
4245
 
4246
+ $this->enqueue_style( 'as3cf-pro-attachment-styles', 'assets/css/attachment', array( 'as3cf-modal' ) );
 
4247
 
4248
+ do_action( 'as3cf_load_attachment_assets' );
4249
  }
4250
 
4251
  /**
4274
  return ( '.' === $directory ) ? '' : $directory . '/';
4275
  }
4276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4277
  /**
4278
  * Has the given attachment been uploaded by this instance?
4279
  *
4290
  }
4291
 
4292
  /**
4293
+ * Filters the audio & video shortcodes output to remove "&_=NN" params from source.src as it breaks signed URLs.
4294
  *
4295
+ * @param string $html Shortcode HTML output.
4296
+ * @param array $atts Array of shortcode attributes.
4297
+ * @param string $media Media file.
4298
  * @param int $post_id Post ID.
4299
+ * @param string $library Media library used for the shortcode.
4300
  *
4301
  * @return string
4302
  *
4303
  * Note: Depends on 30377.4.diff from https://core.trac.wordpress.org/ticket/30377
4304
  */
4305
+ public function wp_media_shortcode( $html, $atts, $media, $post_id, $library ) {
4306
  $html = preg_replace( '/&#038;_=[0-9]+/', '', $html );
4307
 
4308
  return $html;
4326
 
4327
  return $url;
4328
  }
4329
+
4330
+ /**
4331
+ * Get ACL for intermediate size.
4332
+ *
4333
+ * @param int $attachment_id
4334
+ * @param string $size
4335
+ *
4336
+ * @return string
4337
+ */
4338
+ public function get_acl_for_intermediate_size( $attachment_id, $size ) {
4339
+ $s3_info = $this->get_attachment_s3_info( $attachment_id );
4340
+
4341
+ if ( 'original' === $size || empty( $size ) ) {
4342
+ return isset( $s3_info['acl'] ) ? $s3_info['acl'] : self::DEFAULT_ACL;
4343
+ }
4344
+
4345
+ if ( ! empty( $s3_info['sizes'][ $size ]['acl'] ) ) {
4346
+ return $s3_info['sizes'][ $size ]['acl'];
4347
+ }
4348
+
4349
+ return self::DEFAULT_ACL;
4350
+ }
4351
+
4352
+ /**
4353
+ * Consolidate an array of WP_Errors into a single WP_Error object.
4354
+ *
4355
+ * @param array $upload_errors
4356
+ *
4357
+ * @return WP_Error
4358
+ */
4359
+ protected function consolidate_upload_errors( $upload_errors ) {
4360
+ $errors = new WP_Error;
4361
+
4362
+ foreach ( $upload_errors as $error ) {
4363
+
4364
+ /* @var WP_Error $error */
4365
+ $errors->add( $error->get_error_code(), $error->get_error_message() );
4366
+ }
4367
+
4368
+ return $errors;
4369
+ }
4370
  }
classes/as3cf-filter.php CHANGED
@@ -2,6 +2,21 @@
2
 
3
  abstract class AS3CF_Filter {
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  /**
6
  * @var Amazon_S3_And_CloudFront
7
  */
@@ -12,6 +27,11 @@ abstract class AS3CF_Filter {
12
  */
13
  protected $query_cache = array();
14
 
 
 
 
 
 
15
  /**
16
  * Constructor
17
  *
@@ -26,6 +46,13 @@ abstract class AS3CF_Filter {
26
  $this->init();
27
  }
28
 
 
 
 
 
 
 
 
29
  /**
30
  * Filter EDD download files.
31
  *
@@ -118,6 +145,87 @@ abstract class AS3CF_Filter {
118
  return $content;
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  /**
122
  * Process content.
123
  *
@@ -206,6 +314,8 @@ abstract class AS3CF_Filter {
206
  continue;
207
  }
208
 
 
 
209
  if ( ! preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) || ! isset( $class_id[1] ) ) {
210
  // Can't determine ID from class, skip
211
  continue;
@@ -264,7 +374,7 @@ abstract class AS3CF_Filter {
264
  continue;
265
  }
266
 
267
- $parts = parse_url( $url );
268
 
269
  if ( ! isset( $parts['path'] ) ) {
270
  // URL doesn't have a path, continue
@@ -277,7 +387,7 @@ abstract class AS3CF_Filter {
277
  }
278
 
279
  $attachment_id = null;
280
- $bare_url = $this->as3cf->maybe_remove_query_string( $url );
281
 
282
  if ( isset( $cache[ $bare_url ] ) ) {
283
  $attachment_id = $cache[ $bare_url ];
@@ -290,9 +400,9 @@ abstract class AS3CF_Filter {
290
 
291
  if ( is_null( $attachment_id ) || is_array( $attachment_id ) ) {
292
  // Attachment ID not cached, need to search by URL.
293
- $urls[] = $url;
294
  } else {
295
- $this->push_to_url_pairs( $url_pairs, $attachment_id, $url, $to_cache );
296
  }
297
  }
298
 
@@ -351,7 +461,7 @@ abstract class AS3CF_Filter {
351
  return false;
352
  }
353
 
354
- $base_url = $this->as3cf->remove_scheme( $this->as3cf->maybe_remove_query_string( $this->get_base_url( $attachment_id ) ) );
355
  $basename = wp_basename( $base_url );
356
 
357
  // Add full size URL
@@ -362,7 +472,7 @@ abstract class AS3CF_Filter {
362
  $base_urls[] = str_replace( $basename, $size['file'], $base_url );
363
  }
364
 
365
- $url = $this->as3cf->remove_scheme( $this->as3cf->maybe_remove_query_string( $url ) );
366
 
367
  if ( in_array( $url, $base_urls ) ) {
368
  // Match found, return true
@@ -381,7 +491,7 @@ abstract class AS3CF_Filter {
381
  * @param array $to_cache
382
  */
383
  protected function push_to_url_pairs( &$url_pairs, $attachment_id, $find, &$to_cache ) {
384
- $find_full = $this->as3cf->remove_size_from_filename( $find );
385
  $find_full = $this->normalize_find_value( $this->as3cf->maybe_remove_query_string( $find_full ) );
386
  $find_size = $this->normalize_find_value( $this->as3cf->maybe_remove_query_string( $find ) );
387
 
@@ -404,8 +514,8 @@ abstract class AS3CF_Filter {
404
  $parts = parse_url( $find );
405
 
406
  if ( ! isset( $parts['scheme'] ) ) {
407
- $replace_full = $this->as3cf->remove_scheme( $replace_full );
408
- $replace_size = $this->as3cf->remove_scheme( $replace_size );
409
  }
410
 
411
  // Find and replace full version
@@ -458,7 +568,7 @@ abstract class AS3CF_Filter {
458
  * @param array $to_cache
459
  */
460
  protected function url_cache_failure( $url, &$to_cache ) {
461
- $full = $this->as3cf->remove_size_from_filename( $url );
462
  $failure = array(
463
  'timestamp' => time(),
464
  );
@@ -495,19 +605,22 @@ abstract class AS3CF_Filter {
495
  /**
496
  * Get post cache
497
  *
498
- * @param bool|int $post_id
499
  *
500
  * @return array
501
  */
502
- protected function get_post_cache( $post_id = false ) {
503
- $post_id = $this->get_post_id( $post_id );
504
 
505
  if ( ! $post_id ) {
506
- // Post ID not found, return empty cache
507
  return array();
508
  }
509
 
510
- $cache = get_post_meta( $post_id, 'amazonS3_cache', true );
 
 
 
 
511
 
512
  if ( empty( $cache ) ) {
513
  $cache = array();
@@ -517,42 +630,59 @@ abstract class AS3CF_Filter {
517
  }
518
 
519
  /**
520
- * Maybe update post cache
521
  *
522
- * @param array $to_cache
523
- * @param bool|int $post_id
524
  */
525
- protected function maybe_update_post_cache( $to_cache, $post_id = false ) {
526
- $post_id = $this->get_post_id( $post_id );
527
 
528
- if ( ! $post_id || empty( $to_cache ) ) {
529
  return;
530
  }
531
 
532
- $urls = array_merge( $this->get_post_cache( $post_id ), $to_cache );
 
 
 
 
 
 
533
 
534
- update_post_meta( $post_id, 'amazonS3_cache', $urls );
 
 
 
 
 
 
 
 
 
 
 
535
  }
536
 
537
  /**
538
- * Get post ID.
539
  *
 
540
  * @param bool|int $post_id
541
- *
542
- * @return bool|int
543
  */
544
- protected function get_post_id( $post_id ) {
545
- if ( false !== $post_id ) {
546
- return $post_id;
 
 
547
  }
548
 
549
- global $post;
 
550
 
551
- if ( isset( $post->ID ) ) {
552
- return $post->ID;
553
  }
554
-
555
- return false;
556
  }
557
 
558
  /**
@@ -561,7 +691,17 @@ abstract class AS3CF_Filter {
561
  * @return array
562
  */
563
  protected function get_option_cache() {
564
- return get_option( 'amazonS3_cache', array() );
 
 
 
 
 
 
 
 
 
 
565
  }
566
 
567
  /**
@@ -574,9 +714,12 @@ abstract class AS3CF_Filter {
574
  return;
575
  }
576
 
577
- $urls = array_merge( $this->get_option_cache(), $to_cache );
 
578
 
579
- update_option( 'amazonS3_cache', $urls );
 
 
580
  }
581
 
582
  /**
@@ -585,11 +728,17 @@ abstract class AS3CF_Filter {
585
  * @param int $post_id
586
  */
587
  public function purge_cache_on_attachment_delete( $post_id ) {
588
- $this->purge_from_cache( $this->get_url( $post_id ) );
 
 
 
589
  }
590
 
591
  /**
592
- * Purge URL from cache
 
 
 
593
  *
594
  * @param string $url
595
  * @param bool|int $blog_id
@@ -606,7 +755,7 @@ abstract class AS3CF_Filter {
606
  DELETE FROM {$wpdb->postmeta}
607
  WHERE meta_key = %s
608
  AND meta_value LIKE %s;
609
- ", 'amazonS3_cache', '%"' . $url . '"%' );
610
 
611
  $wpdb->query( $sql );
612
 
@@ -615,7 +764,7 @@ abstract class AS3CF_Filter {
615
  DELETE FROM {$wpdb->options}
616
  WHERE option_name = %s
617
  AND option_value LIKE %s;
618
- ", 'amazonS3_cache', '%"' . $url . '"%' );
619
 
620
  $wpdb->query( $sql );
621
 
@@ -691,6 +840,28 @@ abstract class AS3CF_Filter {
691
  return $css;
692
  }
693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
  /**
695
  * Get custom CSS post ID.
696
  *
2
 
3
  abstract class AS3CF_Filter {
4
 
5
+ /**
6
+ * The key used for storing the URL cache.
7
+ */
8
+ const CACHE_KEY = 'amazonS3_cache';
9
+
10
+ /**
11
+ * The cache group used by an external object cache for posts.
12
+ */
13
+ const POST_CACHE_GROUP = 'post_amazonS3_cache';
14
+
15
+ /**
16
+ * The cache group used by an external object cache for options.
17
+ */
18
+ const OPTION_CACHE_GROUP = 'option_amazonS3_cache';
19
+
20
  /**
21
  * @var Amazon_S3_And_CloudFront
22
  */
27
  */
28
  protected $query_cache = array();
29
 
30
+ /**
31
+ * @var array IDs which have already been purged this request.
32
+ */
33
+ protected static $purged_ids = array();
34
+
35
  /**
36
  * Constructor
37
  *
46
  $this->init();
47
  }
48
 
49
+ /**
50
+ * Initialize the filter.
51
+ */
52
+ protected function init() {
53
+ // Optionally override in a sub-class.
54
+ }
55
+
56
  /**
57
  * Filter EDD download files.
58
  *
145
  return $content;
146
  }
147
 
148
+ /**
149
+ * Handle widget instances.
150
+ *
151
+ * @param array $instance
152
+ * @param WP_Widget $class
153
+ *
154
+ * @return array
155
+ */
156
+ protected function handle_widget( $instance, $class ) {
157
+ if ( empty( $instance ) ) {
158
+ return $instance;
159
+ }
160
+
161
+ $update_cache = true;
162
+
163
+ // Editing widgets in Customizer throws an error if more than one option record is updated.
164
+ // Therefore cache updating has to wait until render or edit via Appearance menu.
165
+ if ( isset( $_POST['wp_customize'] ) && 'on' === $_POST['wp_customize'] ) {
166
+ $update_cache = false;
167
+ }
168
+
169
+ if ( $class instanceof WP_Widget_Media ) {
170
+ return $this->filter_media_widget( $instance, $update_cache );
171
+ }
172
+
173
+ if ( $class instanceof WP_Widget_Text ) {
174
+ return $this->filter_text_widget( $instance, $update_cache );
175
+ }
176
+
177
+ return $instance;
178
+ }
179
+
180
+ /**
181
+ * Filter media widget.
182
+ *
183
+ * @param array $instance
184
+ * @param bool $update_cache
185
+ *
186
+ * @return array
187
+ */
188
+ protected function filter_media_widget( $instance, $update_cache ) {
189
+ $cache = $this->get_option_cache();
190
+ $to_cache = array();
191
+
192
+ foreach ( $instance as $key => $value ) {
193
+ if ( empty( $value ) ) {
194
+ continue;
195
+ }
196
+
197
+ if ( AS3CF_Utils::is_url( $value ) ) {
198
+ $instance[ $key ] = $this->process_content( $value, $cache, $to_cache );
199
+ }
200
+ }
201
+
202
+ if ( $update_cache ) {
203
+ $this->maybe_update_option_cache( $to_cache );
204
+ }
205
+
206
+ return $instance;
207
+ }
208
+
209
+ /**
210
+ * Filter text widget.
211
+ *
212
+ * @param array $instance
213
+ * @param bool $update_cache
214
+ *
215
+ * @return array
216
+ */
217
+ protected function filter_text_widget( $instance, $update_cache ) {
218
+ $cache = $this->get_option_cache();
219
+ $to_cache = array();
220
+ $instance['text'] = $this->process_content( $instance['text'], $cache, $to_cache );
221
+
222
+ if ( $update_cache ) {
223
+ $this->maybe_update_option_cache( $to_cache );
224
+ }
225
+
226
+ return $instance;
227
+ }
228
+
229
  /**
230
  * Process content.
231
  *
314
  continue;
315
  }
316
 
317
+ $url = AS3CF_Utils::reduce_url( $url );
318
+
319
  if ( ! preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) || ! isset( $class_id[1] ) ) {
320
  // Can't determine ID from class, skip
321
  continue;
374
  continue;
375
  }
376
 
377
+ $parts = AS3CF_Utils::parse_url( $url );
378
 
379
  if ( ! isset( $parts['path'] ) ) {
380
  // URL doesn't have a path, continue
387
  }
388
 
389
  $attachment_id = null;
390
+ $bare_url = AS3CF_Utils::reduce_url( $url );
391
 
392
  if ( isset( $cache[ $bare_url ] ) ) {
393
  $attachment_id = $cache[ $bare_url ];
400
 
401
  if ( is_null( $attachment_id ) || is_array( $attachment_id ) ) {
402
  // Attachment ID not cached, need to search by URL.
403
+ $urls[] = $bare_url;
404
  } else {
405
+ $this->push_to_url_pairs( $url_pairs, $attachment_id, $bare_url, $to_cache );
406
  }
407
  }
408
 
461
  return false;
462
  }
463
 
464
+ $base_url = AS3CF_Utils::reduce_url( $this->get_base_url( $attachment_id ) );
465
  $basename = wp_basename( $base_url );
466
 
467
  // Add full size URL
472
  $base_urls[] = str_replace( $basename, $size['file'], $base_url );
473
  }
474
 
475
+ $url = AS3CF_Utils::reduce_url( $url );
476
 
477
  if ( in_array( $url, $base_urls ) ) {
478
  // Match found, return true
491
  * @param array $to_cache
492
  */
493
  protected function push_to_url_pairs( &$url_pairs, $attachment_id, $find, &$to_cache ) {
494
+ $find_full = AS3CF_Utils::remove_size_from_filename( $find );
495
  $find_full = $this->normalize_find_value( $this->as3cf->maybe_remove_query_string( $find_full ) );
496
  $find_size = $this->normalize_find_value( $this->as3cf->maybe_remove_query_string( $find ) );
497
 
514
  $parts = parse_url( $find );
515
 
516
  if ( ! isset( $parts['scheme'] ) ) {
517
+ $replace_full = AS3CF_Utils::remove_scheme( $replace_full );
518
+ $replace_size = AS3CF_Utils::remove_scheme( $replace_size );
519
  }
520
 
521
  // Find and replace full version
568
  * @param array $to_cache
569
  */
570
  protected function url_cache_failure( $url, &$to_cache ) {
571
+ $full = AS3CF_Utils::remove_size_from_filename( $url );
572
  $failure = array(
573
  'timestamp' => time(),
574
  );
605
  /**
606
  * Get post cache
607
  *
608
+ * @param null|int|WP_Post $post Optional. Post ID or post object. Defaults to current post.
609
  *
610
  * @return array
611
  */
612
+ public function get_post_cache( $post = null ) {
613
+ $post_id = AS3CF_Utils::get_post_id( $post );
614
 
615
  if ( ! $post_id ) {
 
616
  return array();
617
  }
618
 
619
+ if ( wp_using_ext_object_cache() ) {
620
+ $cache = wp_cache_get( $post_id, self::POST_CACHE_GROUP );
621
+ } else {
622
+ $cache = get_post_meta( $post_id, self::CACHE_KEY, true );
623
+ }
624
 
625
  if ( empty( $cache ) ) {
626
  $cache = array();
630
  }
631
 
632
  /**
633
+ * Set the cache for the given post.
634
  *
635
+ * @param null|int|WP_Post $post Optional. Post ID or post object. Defaults to current post.
636
+ * @param $data
637
  */
638
+ protected function set_post_cache( $post, $data ) {
639
+ $post_id = AS3CF_Utils::get_post_id( $post );
640
 
641
+ if ( ! $post_id ) {
642
  return;
643
  }
644
 
645
+ if ( wp_using_ext_object_cache() ) {
646
+ $expires = apply_filters( 'as3cf_' . self::POST_CACHE_GROUP . '_expires', DAY_IN_SECONDS, $post_id, $data );
647
+ wp_cache_set( $post_id, $data, self::POST_CACHE_GROUP, $expires );
648
+ } else {
649
+ update_post_meta( $post_id, self::CACHE_KEY, $data );
650
+ }
651
+ }
652
 
653
+ /**
654
+ * Set the option cache with the given data.
655
+ *
656
+ * @param $data
657
+ */
658
+ protected function set_option_cache( $data ) {
659
+ if ( wp_using_ext_object_cache() ) {
660
+ $expires = apply_filters( 'as3cf_' . self::OPTION_CACHE_GROUP . '_expires', DAY_IN_SECONDS, self::CACHE_KEY, $data );
661
+ wp_cache_set( self::CACHE_KEY, $data, self::OPTION_CACHE_GROUP, $expires );
662
+ } else {
663
+ update_option( self::CACHE_KEY, $data );
664
+ }
665
  }
666
 
667
  /**
668
+ * Maybe update post cache
669
  *
670
+ * @param array $to_cache
671
  * @param bool|int $post_id
 
 
672
  */
673
+ protected function maybe_update_post_cache( $to_cache, $post_id = false ) {
674
+ $post_id = AS3CF_Utils::get_post_id( $post_id );
675
+
676
+ if ( ! $post_id || empty( $to_cache ) ) {
677
+ return;
678
  }
679
 
680
+ $cached = $this->get_post_cache( $post_id );
681
+ $urls = static::merge_cache( $cached, $to_cache );
682
 
683
+ if ( $urls !== $cached ) {
684
+ $this->set_post_cache( $post_id, $urls );
685
  }
 
 
686
  }
687
 
688
  /**
691
  * @return array
692
  */
693
  protected function get_option_cache() {
694
+ if ( wp_using_ext_object_cache() ) {
695
+ $cache = wp_cache_get( self::CACHE_KEY, self::OPTION_CACHE_GROUP );
696
+ } else {
697
+ $cache = get_option( self::CACHE_KEY, array() );
698
+ }
699
+
700
+ if ( empty( $cache ) ) {
701
+ $cache = array();
702
+ }
703
+
704
+ return $cache;
705
  }
706
 
707
  /**
714
  return;
715
  }
716
 
717
+ $cached = $this->get_option_cache();
718
+ $urls = static::merge_cache( $cached, $to_cache );
719
 
720
+ if ( $urls !== $cached ) {
721
+ $this->set_option_cache( $urls );
722
+ }
723
  }
724
 
725
  /**
728
  * @param int $post_id
729
  */
730
  public function purge_cache_on_attachment_delete( $post_id ) {
731
+ if ( ! in_array( $post_id, self::$purged_ids ) ) {
732
+ $this->purge_from_cache( $this->get_url( $post_id ) );
733
+ self::$purged_ids[] = $post_id;
734
+ }
735
  }
736
 
737
  /**
738
+ * Purge URL from cache.
739
+ *
740
+ * Currently does nothing for purging from an external object cache.
741
+ * Values are left to expire using the expiration time provided when set.
742
  *
743
  * @param string $url
744
  * @param bool|int $blog_id
755
  DELETE FROM {$wpdb->postmeta}
756
  WHERE meta_key = %s
757
  AND meta_value LIKE %s;
758
+ ", self::CACHE_KEY, '%"' . $url . '"%' );
759
 
760
  $wpdb->query( $sql );
761
 
764
  DELETE FROM {$wpdb->options}
765
  WHERE option_name = %s
766
  AND option_value LIKE %s;
767
+ ", self::CACHE_KEY, '%"' . $url . '"%' );
768
 
769
  $wpdb->query( $sql );
770
 
840
  return $css;
841
  }
842
 
843
+ /**
844
+ * Merge content filtering cache arrays.
845
+ *
846
+ * @param array $existing_cache
847
+ * @param array $merge_cache
848
+ *
849
+ * @return array
850
+ */
851
+ public static function merge_cache( $existing_cache, $merge_cache ) {
852
+ if ( ! empty( $existing_cache ) ) {
853
+ $post_cache_keys = array_map( 'AS3CF_Utils::reduce_url', array_keys( $existing_cache ) );
854
+ $existing_cache = array_combine( $post_cache_keys, $existing_cache );
855
+ }
856
+
857
+ if ( ! empty( $merge_cache ) ) {
858
+ $add_cache_keys = array_map( 'AS3CF_Utils::reduce_url', array_keys( $merge_cache ) );
859
+ $merge_cache = array_combine( $add_cache_keys, $merge_cache );
860
+ }
861
+
862
+ return array_merge( $existing_cache, $merge_cache );
863
+ }
864
+
865
  /**
866
  * Get custom CSS post ID.
867
  *
classes/as3cf-notices.php CHANGED
@@ -423,15 +423,8 @@ class AS3CF_Notices {
423
  * Enqueue notice scripts in the admin
424
  */
425
  public function enqueue_notice_scripts() {
426
- $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'];
427
- $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
428
-
429
- // Enqueue notice.css & notice.js globally as some notices can be shown & dismissed on any admin page.
430
- $src = plugins_url( 'assets/css/notice.css', $this->as3cf->get_plugin_file_path() );
431
- wp_enqueue_style( 'as3cf-notice', $src, array(), $version );
432
-
433
- $src = plugins_url( 'assets/js/notice' . $suffix . '.js', $this->as3cf->get_plugin_file_path() );
434
- wp_enqueue_script( 'as3cf-notice', $src, array( 'jquery' ), $version, true );
435
 
436
  wp_localize_script( 'as3cf-notice', 'as3cf_notice', array(
437
  'strings' => array(
423
  * Enqueue notice scripts in the admin
424
  */
425
  public function enqueue_notice_scripts() {
426
+ $this->as3cf->enqueue_style( 'as3cf-notice', 'assets/css/notice' );
427
+ $this->as3cf->enqueue_script( 'as3cf-notice', 'assets/js/notice', array( 'jquery' ) );
 
 
 
 
 
 
 
428
 
429
  wp_localize_script( 'as3cf-notice', 'as3cf_notice', array(
430
  'strings' => array(
classes/as3cf-plugin-compatibility.php CHANGED
@@ -72,9 +72,6 @@ class AS3CF_Plugin_Compatibility {
72
  * Register the compatibility hooks as long as the plugin is setup.
73
  */
74
  function compatibility_init_if_setup() {
75
- // Add notices about compatibility addons to install
76
- add_action( 'admin_init', array( $this, 'maybe_render_compatibility_addons_notice' ) );
77
-
78
  // Turn on stream wrapper S3 file
79
  add_filter( 'as3cf_get_attached_file', array( $this, 'get_stream_wrapper_file' ), 20, 4 );
80
 
@@ -88,7 +85,7 @@ class AS3CF_Plugin_Compatibility {
88
  * WP_Image_Editor
89
  * /wp-includes/class-wp-image-editor.php
90
  */
91
- add_action( 'as3cf_upload_attachment_pre_remove', array( $this, 'image_editor_remove_files' ), 10, 4 );
92
  add_filter( 'as3cf_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
93
  add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'image_editor_remove_original_image' ), 10, 3 );
94
  add_filter( 'as3cf_get_attached_file', array( $this, 'customizer_crop_download_file' ), 10, 4 );
@@ -100,202 +97,13 @@ class AS3CF_Plugin_Compatibility {
100
  * https://wordpress.org/plugins/regenerate-thumbnails/
101
  */
102
  add_filter( 'as3cf_get_attached_file', array( $this, 'regenerate_thumbnails_download_file' ), 10, 4 );
103
- }
104
-
105
- /**
106
- * Get the addons for the Pro upgrade
107
- *
108
- * @return array
109
- */
110
- public function get_pro_addons() {
111
- global $amazon_web_services;
112
-
113
- $all_addons = $amazon_web_services->get_addons( true );
114
- if ( ! isset( $all_addons['amazon-s3-and-cloudfront-pro']['addons'] ) ) {
115
- return array();
116
- }
117
-
118
- $addons = $all_addons['amazon-s3-and-cloudfront-pro']['addons'];
119
-
120
- return $addons;
121
- }
122
-
123
- /**
124
- * Get compatibility addons that are required to be installed
125
- *
126
- * @return array
127
- */
128
- public function get_compatibility_addons_to_install() {
129
- if ( isset( $this->compatibility_addons ) ) {
130
- return $this->compatibility_addons;
131
- }
132
-
133
- $addons = $this->get_pro_addons();
134
- $addons_to_install = array();
135
-
136
- if ( empty ( $addons ) ) {
137
- return $addons_to_install;
138
- }
139
-
140
- foreach ( $addons as $addon_slug => $addon ) {
141
- if ( file_exists( WP_PLUGIN_DIR . '/' . $addon_slug . '/' . $addon_slug . '.php' ) ) {
142
- // Addon already installed, ignore.
143
- continue;
144
- }
145
-
146
- if ( ! isset( $addon['parent_plugin_basename'] ) || '' === $addon['parent_plugin_basename'] ) {
147
- // Addon doesn't have a parent plugin, ignore.
148
- continue;
149
- }
150
-
151
- if ( ! file_exists( WP_PLUGIN_DIR . '/' . $addon['parent_plugin_basename'] ) || ! is_plugin_active( $addon['parent_plugin_basename'] ) ) {
152
- // Parent plugin not installed or not activated, ignore.
153
- continue;
154
- }
155
-
156
- $addons_to_install[ $addon_slug ] = array(
157
- 'title' => $addon['title'],
158
- 'url' => $addon['url'],
159
- );
160
- }
161
-
162
- $this->compatibility_addons = $addons_to_install;
163
-
164
- return $addons_to_install;
165
- }
166
-
167
- /**
168
- * Maybe show a notice about installing addons when the site is using the
169
- * plugins they add compatibility for.
170
- */
171
- public function maybe_render_compatibility_addons_notice() {
172
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
173
- return;
174
- }
175
-
176
- global $as3cf_compat_check;
177
- if ( ! $as3cf_compat_check->check_capabilities() ) {
178
- // User can't install plugins anyway, bail.
179
- return;
180
- }
181
-
182
- if ( ! $this->should_show_compatibility_notice() ) {
183
- // No addons to install, or addons haven't changed
184
- return;
185
- }
186
-
187
- $notice_id = 'as3cf-compat-addons';
188
- $addons_to_install = $this->get_compatibility_addons_to_install();
189
-
190
- // Remove previous notice to refresh addon list
191
- $this->remove_compatibility_notice();
192
-
193
- $title = __( 'WP Offload S3 Compatibility Addons', 'amazon-s3-and-cloudfront' );
194
- $compat_url = 'https://deliciousbrains.com/wp-offload-s3/doc/compatibility-with-other-plugins/';
195
- $compat_link = sprintf( '<a href="%s">%s</a>', $compat_url, __( 'compatibility addons', 'amazon-s3-and-cloudfront' ) );
196
- $message = sprintf( __( "To get WP Offload S3 to work with certain 3rd party plugins, you might need to install and activate some of our %s. We've detected the following addons might need to be installed. Please click the links for more information about each addon to determine if you need it or not.", 'amazon-s3-and-cloudfront' ), $compat_link );
197
-
198
- $notice_addons_text = $this->render_addon_list( $addons_to_install );
199
- $support_email = 'nom@deliciousbrains.com';
200
- $support_link = sprintf( '<a href="mailto:%1$s">%1$s</a>', $support_email );
201
-
202
- $notice_addons_text .= '<p>' . sprintf( __( "You will need to purchase a license to get access to these addons. If you're having trouble determining whether or not you need the addons, send an email to %s.", 'amazon-s3-and-cloudfront' ), $support_link ) . '</p>';
203
- $notice_addons_text .= sprintf( '<p><a href="%s" class="button button-large">%s</a></p>', 'https://deliciousbrains.com/wp-offload-s3/pricing/', __( 'View Licenses', 'amazon-s3-and-cloudfront' ) );
204
-
205
- $notice_addons_text = apply_filters( 'wpos3_compat_addons_notice', $notice_addons_text, $addons_to_install );
206
-
207
- if ( false === $notice_addons_text ) {
208
- // Allow the notice to be aborted.
209
- return;
210
- }
211
-
212
- $notice = '<p><strong>' . $title . '</strong> &mdash; ' . $message . '</p>' . $notice_addons_text;
213
-
214
- $notice_args = array(
215
- 'type' => 'notice-info',
216
- 'custom_id' => $notice_id,
217
- 'only_show_to_user' => false,
218
- 'flash' => false,
219
- 'auto_p' => false,
220
- );
221
-
222
- $notice_args = apply_filters( 'wpos3_compat_addons_notice_args', $notice_args, $addons_to_install );
223
-
224
- update_site_option( 'as3cf_compat_addons_to_install', $addons_to_install );
225
 
226
- $this->as3cf->notices->add_notice( $notice, $notice_args );
227
- }
228
-
229
- /**
230
- * Should show compatibility notice
231
- *
232
- * @return bool
233
- */
234
- protected function should_show_compatibility_notice() {
235
- $addons = $this->get_compatibility_addons_to_install();
236
- $previous_addons = get_site_option( 'as3cf_compat_addons_to_install', array() );
237
-
238
- if ( empty( $addons ) && empty( $previous_addons ) ) {
239
- // No addons to install
240
- return false;
241
- }
242
-
243
- if ( empty( $addons ) && ! empty( $previous_addons ) ) {
244
- // No addons to install but previous exist
245
- $this->remove_compatibility_notice( true );
246
-
247
- return false;
248
- }
249
-
250
- if ( $previous_addons === $addons ) {
251
- // Addons have not changed
252
- return false;
253
- }
254
-
255
- return true;
256
- }
257
-
258
- /**
259
- * Remove compatibility notice
260
- *
261
- * @param bool $delete_option
262
- */
263
- protected function remove_compatibility_notice( $delete_option = false ) {
264
- $notice_id = 'as3cf-compat-addons';
265
-
266
- if ( $this->as3cf->notices->find_notice_by_id( $notice_id ) ) {
267
- $this->as3cf->notices->undismiss_notice_for_all( $notice_id );
268
- $this->as3cf->notices->remove_notice_by_id( $notice_id );
269
- }
270
-
271
- if ( $delete_option ) {
272
- delete_site_option( 'as3cf_compat_addons_to_install' );
273
- }
274
- }
275
-
276
- /**
277
- * Render list of addons for a notice
278
- *
279
- * @param array $addons
280
- *
281
- * @return string
282
- */
283
- protected function render_addon_list( $addons ) {
284
- if ( ! is_array( $addons ) || empty( $addons ) ) {
285
- return '';
286
- }
287
-
288
- sort( $addons );
289
-
290
- $html = '<ul style="list-style-type: disc; padding: 0 0 0 30px; margin: 5px 0;">';
291
- foreach ( $addons as $addon ) {
292
- $html .= '<li style="margin: 0;">';
293
- $html .= '<a href="' . $addon['url'] . '">' . $addon['title'] . '</a>';
294
- $html .= '</li>';
295
  }
296
- $html .= '</ul>';
297
-
298
- return $html;
299
  }
300
 
301
  /**
@@ -325,6 +133,13 @@ class AS3CF_Plugin_Compatibility {
325
  return $url;
326
  }
327
 
 
 
 
 
 
 
 
328
  /**
329
  * Is this an AJAX process?
330
  *
@@ -426,21 +241,49 @@ class AS3CF_Plugin_Compatibility {
426
  * Allow the WordPress Image Editor to remove edited version of images
427
  * if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
428
  *
429
- * @param int $post_id
430
- * @param array $s3object
431
- * @param string $prefix
432
- * @param array $args
 
433
  */
434
- function image_editor_remove_files( $post_id, $s3object, $prefix, $args ) {
435
  if ( ! isset( $_POST['do'] ) || 'restore' !== $_POST['do'] ) {
436
- return;
437
  }
438
 
439
  if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  return;
441
  }
442
 
443
- $this->as3cf->remove_attachment_files_from_s3( $post_id, $s3object, false );
444
  }
445
 
446
  /**
@@ -475,19 +318,19 @@ class AS3CF_Plugin_Compatibility {
475
  $this->copy_s3_file_to_server( $orig_s3, $orig_file );
476
 
477
  // Copy the edited file back to the server as well, it will be cleaned up later
478
- if ( ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
479
  // Return the file if successfully downloaded from S3
480
  return $s3_file;
481
  };
482
  }
483
 
484
- // must be the image-editor process
485
- if ( isset( $_POST['action'] ) && 'image-editor' == sanitize_key( $_POST['action'] ) ) { // input var okay
486
- $callers = debug_backtrace();
487
- foreach ( $callers as $caller ) {
488
  if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
489
  // check this has been called by '_load_image_to_edit_path' so as only to copy back once
490
- if ( ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
491
  // Return the file if successfully downloaded from S3
492
  return $s3_file;
493
  };
72
  * Register the compatibility hooks as long as the plugin is setup.
73
  */
74
  function compatibility_init_if_setup() {
 
 
 
75
  // Turn on stream wrapper S3 file
76
  add_filter( 'as3cf_get_attached_file', array( $this, 'get_stream_wrapper_file' ), 20, 4 );
77
 
85
  * WP_Image_Editor
86
  * /wp-includes/class-wp-image-editor.php
87
  */
88
+ add_action( 'as3cf_pre_upload_attachment', array( $this, 'image_editor_remove_files' ), 10, 3 );
89
  add_filter( 'as3cf_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
90
  add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'image_editor_remove_original_image' ), 10, 3 );
91
  add_filter( 'as3cf_get_attached_file', array( $this, 'customizer_crop_download_file' ), 10, 4 );
97
  * https://wordpress.org/plugins/regenerate-thumbnails/
98
  */
99
  add_filter( 'as3cf_get_attached_file', array( $this, 'regenerate_thumbnails_download_file' ), 10, 4 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ /*
102
+ * WP-CLI Compatibility
103
+ */
104
+ if ( defined( 'WP_CLI' ) && class_exists( 'WP_CLI') ) {
105
+ WP_CLI::add_hook( 'before_invoke:media regenerate', array( $this, 'enable_get_attached_file_copy_back_to_local' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
 
 
 
107
  }
108
 
109
  /**
133
  return $url;
134
  }
135
 
136
+ /**
137
+ * Enables copying missing local files back to the server when `get_attached_file` filter is called.
138
+ */
139
+ public function enable_get_attached_file_copy_back_to_local() {
140
+ add_filter( 'as3cf_get_attached_file_copy_back_to_local', '__return_true' );
141
+ }
142
+
143
  /**
144
  * Is this an AJAX process?
145
  *
241
  * Allow the WordPress Image Editor to remove edited version of images
242
  * if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
243
  *
244
+ * @param bool $pre
245
+ * @param int $post_id
246
+ * @param array $data
247
+ *
248
+ * @return bool
249
  */
250
+ public function image_editor_remove_files( $pre, $post_id, $data ) {
251
  if ( ! isset( $_POST['do'] ) || 'restore' !== $_POST['do'] ) {
252
+ return $pre;
253
  }
254
 
255
  if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
256
+ return $pre;
257
+ }
258
+
259
+ $s3object = $this->as3cf->get_attachment_s3_info( $post_id );
260
+ $this->remove_edited_image_files( $post_id, $s3object );
261
+
262
+ // Update object key with original filename
263
+ $restored_filename = wp_basename( $data['file'] );
264
+ $old_filename = wp_basename( $s3object['key'] );
265
+ $s3object['key'] = str_replace( $old_filename, $restored_filename, $s3object['key'] );
266
+ update_post_meta( $post_id, 'amazonS3_info', $s3object );
267
+
268
+ return true;
269
+ }
270
+
271
+ /**
272
+ * Remove edited image files from S3.
273
+ *
274
+ * @param int $attachment_id
275
+ * @param array $s3object
276
+ */
277
+ protected function remove_edited_image_files( $attachment_id, $s3object ) {
278
+ $bucket = $s3object['bucket'];
279
+ $region = $this->as3cf->get_s3object_region( $s3object );
280
+ $keys = AS3CF_Utils::get_attachment_edited_keys( $attachment_id, $s3object );
281
+
282
+ if ( empty( $keys ) ) {
283
  return;
284
  }
285
 
286
+ $this->as3cf->delete_s3_objects( $region, $bucket, $keys );
287
  }
288
 
289
  /**
318
  $this->copy_s3_file_to_server( $orig_s3, $orig_file );
319
 
320
  // Copy the edited file back to the server as well, it will be cleaned up later
321
+ if ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) {
322
  // Return the file if successfully downloaded from S3
323
  return $s3_file;
324
  };
325
  }
326
 
327
+ $action = filter_input( INPUT_GET, 'action' ) ?: filter_input( INPUT_POST, 'action' );
328
+
329
+ if ( in_array( $action, array( 'image-editor', 'imgedit-preview' ) ) ) { // input var okay
330
+ foreach ( debug_backtrace() as $caller ) {
331
  if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
332
  // check this has been called by '_load_image_to_edit_path' so as only to copy back once
333
+ if ( $s3_file = $this->copy_s3_file_to_server( $s3_object, $file ) ) {
334
  // Return the file if successfully downloaded from S3
335
  return $s3_file;
336
  };
classes/as3cf-upgrade.php DELETED
@@ -1,600 +0,0 @@
1
- <?php
2
- /**
3
- * Upgrade
4
- *
5
- * @package amazon-s3-and-cloudfront
6
- * @subpackage Classes/Upgrade
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 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 = 0;
35
-
36
- /**
37
- * @var string
38
- */
39
- protected $upgrade_name = 'base';
40
-
41
- /**
42
- * @var string 'metadata', 'attachment'
43
- */
44
- protected $upgrade_type = 'attachment';
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
- /**
82
- * @var string
83
- */
84
- protected $lock_key = 'as3cf_upgrade_lock';
85
-
86
- const STATUS_RUNNING = 1;
87
- const STATUS_ERROR = 2;
88
- const STATUS_PAUSED = 3;
89
-
90
- /**
91
- * Start it up
92
- *
93
- * @param Amazon_S3_And_CloudFront $as3cf - the instance of the as3cf class
94
- */
95
- public function __construct( $as3cf ) {
96
- $this->as3cf = $as3cf;
97
-
98
- $this->running_update_text = $this->get_running_update_text();
99
- $this->cron_hook = 'as3cf_cron_update_' . $this->upgrade_name;
100
- $this->cron_schedule_key = 'as3cf_update_' . $this->upgrade_name . '_interval';
101
-
102
- $this->cron_interval_in_minutes = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_interval', 2 );
103
- $this->error_threshold = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_error_threshold', 20 );
104
-
105
- add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
106
- add_action( $this->cron_hook, array( $this, 'do_upgrade' ) );
107
-
108
- add_action( 'as3cf_pre_settings_render', array( $this, 'maybe_display_notices' ) );
109
- add_action( 'admin_init', array( $this, 'maybe_handle_action' ) );
110
-
111
- // Do default checks if the upgrade can be started
112
- if ( $this->maybe_init() ) {
113
- $this->init();
114
- }
115
- }
116
-
117
- /**
118
- * Can we start the upgrade using default checks
119
- *
120
- * @return bool
121
- */
122
- protected function maybe_init() {
123
- if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
124
- return false;
125
- }
126
-
127
- // make sure this only fires inside the network admin for multisites
128
- if ( is_multisite() && ! is_network_admin() ) {
129
- return false;
130
- }
131
-
132
- // Is plugin setup?
133
- if ( ! $this->as3cf->is_plugin_setup() ) {
134
- return false;
135
- }
136
-
137
- // Have we completed the upgrade?
138
- if ( $this->get_saved_upgrade_id() >= $this->upgrade_id ) {
139
- return false;
140
- }
141
-
142
- // Has the previous upgrade completed yet?
143
- if ( ! $this->has_previous_upgrade_completed() ) {
144
- return false;
145
- }
146
-
147
- // Does the upgrade lock exist?
148
- if ( false !== get_site_transient( $this->lock_key ) ) {
149
- return false;
150
- }
151
-
152
- // Do we actually have attachments to process?
153
- if ( 0 === $this->count_items_to_process() ) {
154
- $this->upgrade_finished();
155
-
156
- return false;
157
- }
158
-
159
- // If the upgrade status is already set, then we've already initialized the upgrade
160
- if ( $upgrade_status = $this->get_upgrade_status() ) {
161
- if ( self::STATUS_RUNNING === $upgrade_status ) {
162
- // Make sure cron job is persisted in case it has dropped
163
- $this->schedule();
164
- }
165
-
166
- return false;
167
- }
168
-
169
- return true;
170
- }
171
-
172
- /**
173
- * Count items to process.
174
- *
175
- * @return int
176
- */
177
- abstract protected function count_items_to_process();
178
-
179
- /**
180
- * Get items to process.
181
- *
182
- * @param string $prefix
183
- * @param int $limit
184
- * @param bool|mixed $offset
185
- *
186
- * @return array
187
- */
188
- abstract protected function get_items_to_process( $prefix, $limit, $offset = false );
189
-
190
- /**
191
- * Upgrade attachment.
192
- *
193
- * @param mixed $attachment
194
- *
195
- * @return bool
196
- */
197
- abstract protected function upgrade_item( $attachment );
198
-
199
- /**
200
- * Get running update text.
201
- *
202
- * @return string
203
- */
204
- abstract protected function get_running_update_text();
205
-
206
- /**
207
- * Fire up the upgrade
208
- */
209
- protected function init() {
210
- // Initialize the upgrade
211
- $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
212
-
213
- $this->schedule();
214
- }
215
-
216
- /**
217
- * Cron job to update the region of the bucket in s3 metadata
218
- */
219
- public function do_upgrade() {
220
- $this->lock_upgrade();
221
-
222
- // Check if the cron should even be running
223
- if ( $this->get_saved_upgrade_id() >= $this->upgrade_id || $this->get_upgrade_status() !== self::STATUS_RUNNING ) {
224
- $this->unschedule();
225
-
226
- return;
227
- }
228
-
229
- // set the batch size limit for the query
230
- $limit = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_batch_size', 500 );
231
- $all_limit = $limit;
232
- $finish = time() + apply_filters( 'as3cf_update_' . $this->upgrade_name . '_time_limit', 20 );
233
-
234
- $session = $this->get_session();
235
-
236
- // find the blog IDs that have been processed so we can skip them
237
- $processed_blog_ids = isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
238
- $this->error_count = isset( $session['error_count'] ) ? $session['error_count'] : 0;
239
- $offset = isset( $session['offset'] ) ? $session['offset'] : false;
240
-
241
- // get the table prefixes for all the blogs
242
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes( $processed_blog_ids );
243
-
244
- $all_items = array();
245
- $all_count = 0;
246
-
247
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
248
- $items = $this->get_items_to_process( $table_prefix, $limit, $offset );
249
- $count = count( $items );
250
-
251
- if ( 0 === $count ) {
252
- // No more items, record the blog ID to skip next time
253
- $session['offset'] = false;
254
- $processed_blog_ids[] = $blog_id;
255
- } else {
256
- $all_count += $count;
257
- $all_items[ $blog_id ] = $items;
258
- }
259
-
260
- if ( $all_count >= $all_limit ) {
261
- break;
262
- }
263
-
264
- $limit = $limit - $count;
265
- }
266
-
267
- if ( 0 === $all_count ) {
268
- $this->upgrade_finished();
269
-
270
- return;
271
- }
272
-
273
- // loop through and update s3 meta with region
274
- foreach ( $all_items as $blog_id => $items ) {
275
- $this->as3cf->switch_to_blog( $blog_id );
276
-
277
- foreach ( $items as $item ) {
278
- if ( $this->error_count >= $this->error_threshold ) {
279
- $this->upgrade_error( $session );
280
-
281
- return;
282
- }
283
-
284
- // Do the actual upgrade to the item
285
- $this->upgrade_item( $item );
286
-
287
- if ( time() >= $finish || $this->as3cf->memory_exceeded( 'as3cf_update_' . $this->upgrade_name . '_memory_exceeded' ) ) {
288
- // Batch limits reached
289
- $this->as3cf->restore_current_blog();
290
-
291
- break 2;
292
- }
293
- }
294
-
295
- $this->as3cf->restore_current_blog();
296
- }
297
-
298
- $session['offset'] = isset( $item ) ? $item : false;
299
- $session['processed_blog_ids'] = $processed_blog_ids;
300
- $session['error_count'] = $this->error_count;
301
-
302
- $this->save_session( $session );
303
- }
304
-
305
- /**
306
- * Adds notices about issues with upgrades allowing user to restart them
307
- */
308
- public function maybe_display_notices() {
309
- $action_url = $this->as3cf->get_plugin_page_url( array(
310
- 'action' => 'restart_update',
311
- 'update' => $this->upgrade_name,
312
- ), 'self' );
313
- $msg_type = 'notice-info';
314
-
315
- switch ( $this->get_upgrade_status() ) {
316
- case self::STATUS_RUNNING:
317
- $msg = $this->get_running_message();
318
- $action_text = __( 'Pause Update', 'amazon-s3-and-cloudfront' );
319
- $action_url = $this->as3cf->get_plugin_page_url( array(
320
- 'action' => 'pause_update',
321
- 'update' => $this->upgrade_name,
322
- ), 'self' );
323
- break;
324
- case self::STATUS_PAUSED:
325
- $msg = $this->get_paused_message();
326
- $action_text = __( 'Restart Update', 'amazon-s3-and-cloudfront' );
327
- break;
328
- case self::STATUS_ERROR:
329
- $msg = $this->get_error_message();
330
- $action_text = __( 'Try Run It Again', 'amazon-s3-and-cloudfront' );
331
- $msg_type = 'error';
332
- break;
333
- default:
334
- return;
335
- }
336
-
337
- $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
338
-
339
- $args = array(
340
- 'message' => $msg,
341
- 'type' => $msg_type,
342
- );
343
-
344
- $this->as3cf->render_view( 'notice', $args );
345
- }
346
-
347
- /**
348
- * Get running message.
349
- *
350
- * @return string
351
- */
352
- protected function get_running_message() {
353
- return sprintf( __( '<strong>Running %1$s Update%2$s</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 %3$s This will be done quietly in the background, processing a small batch of Media Library items every %4$d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'amazon-s3-and-cloudfront' ),
354
- ucwords( $this->upgrade_type ),
355
- $this->get_progress_text(),
356
- $this->running_update_text,
357
- $this->cron_interval_in_minutes
358
- );
359
- }
360
-
361
- /**
362
- * Get paused message.
363
- *
364
- * @return string
365
- */
366
- protected function get_paused_message() {
367
- return sprintf( __( '<strong>%1$s Update Paused%2$s</strong> &mdash; Updating Media Library %3$s has been paused.', 'amazon-s3-and-cloudfront' ),
368
- ucwords( $this->upgrade_type ),
369
- $this->get_progress_text(),
370
- $this->upgrade_type
371
- );
372
- }
373
-
374
- /**
375
- * Get error message.
376
- *
377
- * @return string
378
- */
379
- protected function get_error_message() {
380
- return sprintf( __( '<strong>Error Updating %1$s</strong> &mdash; We ran into some errors attempting to update the %2$s for all your Media Library items that have been uploaded to S3. Please check your error log for details. (#%3$d)', 'amazon-s3-and-cloudfront' ),
381
- ucwords( $this->upgrade_type ),
382
- $this->upgrade_type,
383
- $this->upgrade_id
384
- );
385
- }
386
-
387
- /**
388
- * Get progress text.
389
- *
390
- * @return string
391
- */
392
- protected function get_progress_text() {
393
- $progress = $this->calculate_progress();
394
-
395
- if ( false === $progress ) {
396
- // Progress can not be calculated, return
397
- return '';
398
- }
399
-
400
- if ( $progress > 100 ) {
401
- $progress = 100;
402
- }
403
-
404
- return sprintf( __( ' (%s%% Complete)', 'amazon-s3-and-cloudfront' ), $progress );
405
- }
406
-
407
- /**
408
- * Calculate progress.
409
- *
410
- * @return bool|int|float
411
- */
412
- protected function calculate_progress() {
413
- return false;
414
- }
415
-
416
- /**
417
- * Handler for the running upgrade actions
418
- */
419
- public function maybe_handle_action() {
420
- if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== $this->as3cf->get_plugin_slug() ) { // input var okay
421
- return;
422
- }
423
-
424
- if ( ! isset( $_GET['action'] ) ) {
425
- return;
426
- }
427
-
428
- if ( ! isset( $_GET['update'] ) || sanitize_key( $_GET['update'] ) !== $this->upgrade_name ) { // input var okay
429
- return;
430
- }
431
-
432
- $method_name = 'action_' . sanitize_key( $_GET['action'] ); // input var okay
433
-
434
- if ( method_exists( $this, $method_name ) ) {
435
- call_user_func( array( $this, $method_name ) );
436
- }
437
- }
438
-
439
- /**
440
- * Exit upgrade with an error
441
- *
442
- * @param array $session
443
- */
444
- protected function upgrade_error( $session ) {
445
- $session['status'] = self::STATUS_ERROR;
446
- $this->save_session( $session );
447
- $this->unschedule();
448
- }
449
-
450
- /**
451
- * Complete the upgrade
452
- */
453
- protected function upgrade_finished() {
454
- $this->clear_session();
455
- $this->update_saved_upgrade_id();
456
- $this->unschedule();
457
- }
458
-
459
- /**
460
- * Restart upgrade
461
- */
462
- protected function action_restart_update() {
463
- $this->schedule();
464
- $this->change_status_request( self::STATUS_RUNNING );
465
- }
466
-
467
- /**
468
- * Pause upgrade
469
- */
470
- protected function action_pause_update() {
471
- $this->unschedule();
472
- $this->change_status_request( self::STATUS_PAUSED );
473
- }
474
-
475
- /**
476
- * Helper for the above action requests
477
- *
478
- * @param int $status
479
- */
480
- protected function change_status_request( $status ) {
481
- $session = $this->get_session();
482
- $session['status'] = $status;
483
- $this->save_session( $session );
484
-
485
- $url = $this->as3cf->get_plugin_page_url( array(), 'self' );
486
- wp_redirect( $url );
487
- exit;
488
- }
489
-
490
- /**
491
- * Schedule the cron
492
- */
493
- protected function schedule() {
494
- $this->as3cf->schedule_event( $this->cron_hook, $this->cron_schedule_key );
495
- }
496
-
497
- /**
498
- * Remove the cron schedule
499
- */
500
- protected function unschedule() {
501
- $this->as3cf->clear_scheduled_event( $this->cron_hook );
502
- }
503
-
504
- /**
505
- * Add custom cron interval schedules
506
- *
507
- * @param array $schedules
508
- *
509
- * @return array
510
- */
511
- public function cron_schedules( $schedules ) {
512
- // Add the upgrade interval to the existing schedules.
513
- $schedules[ $this->cron_schedule_key ] = array(
514
- 'interval' => $this->cron_interval_in_minutes * 60,
515
- 'display' => sprintf( __( 'Every %d Minutes', 'amazon-s3-and-cloudfront' ), $this->cron_interval_in_minutes ),
516
- );
517
-
518
- return $schedules;
519
- }
520
-
521
- /**
522
- * Get the current status of the upgrade
523
- * See STATUS_* constants in the class declaration above.
524
- */
525
- protected function get_upgrade_status() {
526
- $session = $this->get_session();
527
-
528
- if ( ! isset( $session['status'] ) ) {
529
- return '';
530
- }
531
-
532
- return $session['status'];
533
- }
534
-
535
- /**
536
- * Retrieve session data from plugin settings
537
- *
538
- * @return array
539
- */
540
- protected function get_session() {
541
- return get_site_option( 'update_' . $this->upgrade_name . '_session', array() );
542
- }
543
-
544
- /**
545
- * Store data to be used between requests in plugin settings
546
- *
547
- * @param array $session session data to store
548
- */
549
- protected function save_session( $session ) {
550
- update_site_option( 'update_' . $this->upgrade_name . '_session', $session );
551
- }
552
-
553
- /**
554
- * Remove the session data to be used between requests
555
- *
556
- */
557
- protected function clear_session() {
558
- delete_site_option( 'update_' . $this->upgrade_name . '_session' );
559
- }
560
-
561
- /**
562
- * Get the saved upgrade ID
563
- *
564
- * @return int|mixed|string|WP_Error
565
- */
566
- protected function get_saved_upgrade_id() {
567
- return $this->as3cf->get_setting( $this->settings_key, 0 );
568
- }
569
-
570
- /**
571
- * Update the saved upgrade ID
572
- */
573
- protected function update_saved_upgrade_id() {
574
- $this->as3cf->set_setting( $this->settings_key, $this->upgrade_id );
575
- $this->as3cf->save_settings();
576
- }
577
-
578
- /**
579
- * Has previous upgrade completed
580
- *
581
- * @return bool
582
- */
583
- protected function has_previous_upgrade_completed() {
584
- // Has the previous upgrade completed yet?
585
- $previous_id = $this->upgrade_id - 1;
586
- if ( 0 !== $previous_id && (int) $this->get_saved_upgrade_id() < $previous_id ) {
587
- // Previous still running, abort
588
- return false;
589
- }
590
-
591
- return true;
592
- }
593
-
594
- /**
595
- * Lock upgrade.
596
- */
597
- protected function lock_upgrade() {
598
- set_site_transient( $this->lock_key, $this->upgrade_id, MINUTE_IN_SECONDS * 3 );
599
- }
600
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/as3cf-utils.php CHANGED
@@ -23,6 +23,16 @@ if ( ! class_exists( 'AS3CF_Utils' ) ) {
23
  *
24
  */
25
  class AS3CF_Utils {
 
 
 
 
 
 
 
 
 
 
26
 
27
  /**
28
  * Checks if another version of WP Offload S3 (Lite) is active and deactivates it.
@@ -94,5 +104,258 @@ if ( ! class_exists( 'AS3CF_Utils' ) ) {
94
 
95
  return $plugin_data['Version'];
96
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
- }
23
  *
24
  */
25
  class AS3CF_Utils {
26
+ /**
27
+ * Get post ID.
28
+ *
29
+ * @param null|int|WP_Post $post Optional. Post ID or post object. Defaults to current post.
30
+ *
31
+ * @return int
32
+ */
33
+ public static function get_post_id( $post = null ) {
34
+ return (int) get_post_field( 'ID', $post );
35
+ }
36
 
37
  /**
38
  * Checks if another version of WP Offload S3 (Lite) is active and deactivates it.
104
 
105
  return $plugin_data['Version'];
106
  }
107
+
108
+ /**
109
+ * Trailing slash prefix string ensuring no leading slashes.
110
+ *
111
+ * @param $string
112
+ *
113
+ * @return string
114
+ */
115
+ public static function trailingslash_prefix( $string ) {
116
+ return ltrim( trailingslashit( $string ), '/\\' );
117
+ }
118
+
119
+ /**
120
+ * Remove scheme from URL.
121
+ *
122
+ * @param string $url
123
+ *
124
+ * @return string
125
+ */
126
+ public static function remove_scheme( $url ) {
127
+ return preg_replace( '/^(?:http|https):/', '', $url );
128
+ }
129
+
130
+ /**
131
+ * Remove size from filename (image[-100x100].jpeg).
132
+ *
133
+ * @param string $url
134
+ * @param bool $remove_extension
135
+ *
136
+ * @return string
137
+ */
138
+ public static function remove_size_from_filename( $url, $remove_extension = false ) {
139
+ $url = preg_replace( '/^(\S+)-[0-9]{1,4}x[0-9]{1,4}(\.[a-zA-Z0-9\.]{2,})?/', '$1$2', $url );
140
+
141
+ if ( $remove_extension ) {
142
+ $ext = pathinfo( $url, PATHINFO_EXTENSION );
143
+ $url = str_replace( ".$ext", '', $url );
144
+ }
145
+
146
+ return $url;
147
+ }
148
+
149
+ /**
150
+ * Reduce the given URL down to the simplest version of itself.
151
+ *
152
+ * Useful for matching against the full version of the URL in a full-text search
153
+ * or saving as a key for dictionary type lookup.
154
+ *
155
+ * @param string $url
156
+ *
157
+ * @return string
158
+ */
159
+ public static function reduce_url( $url ) {
160
+ $parts = self::parse_url( $url );
161
+ $host = isset( $parts['host'] ) ? $parts['host'] : '';
162
+ $port = isset( $parts['port'] ) ? ":{$parts['port']}" : '';
163
+ $path = isset( $parts['path'] ) ? $parts['path'] : '';
164
+
165
+ return '//' . $host . $port . $path;
166
+ }
167
+
168
+ /**
169
+ * Parses a URL into its components. Compatible with PHP < 5.4.7.
170
+ *
171
+ * @param $url string The URL to parse.
172
+ *
173
+ * @return array|false The parsed components or false on error.
174
+ */
175
+ public static function parse_url( $url ) {
176
+ $url = trim( $url );
177
+ $no_scheme = 0 === strpos( $url, '//' );
178
+
179
+ if ( $no_scheme ) {
180
+ $url = 'http:' . $url;
181
+ }
182
+
183
+ $parts = parse_url( $url );
184
+
185
+ if ( $no_scheme ) {
186
+ unset( $parts['scheme'] );
187
+ }
188
+
189
+ return $parts;
190
+ }
191
+
192
+ /**
193
+ * Is the string a URL?
194
+ *
195
+ * @param string $string
196
+ *
197
+ * @return bool
198
+ */
199
+ public static function is_url( $string ) {
200
+ if ( preg_match( '@^(?:https?:)?\/\/[a-zA-Z0-9\-]{3,}@', $string ) ) {
201
+ return true;
202
+ }
203
+
204
+ return false;
205
+ }
206
+
207
+ /**
208
+ * Get file paths for all attachment versions.
209
+ *
210
+ * @param int $attachment_id
211
+ * @param bool $exists_locally
212
+ * @param array|bool $meta
213
+ * @param bool $include_backups
214
+ *
215
+ * @return array
216
+ */
217
+ public static function get_attachment_file_paths( $attachment_id, $exists_locally = true, $meta = false, $include_backups = true ) {
218
+ $file_path = get_attached_file( $attachment_id, true );
219
+ $paths = array(
220
+ 'original' => $file_path,
221
+ );
222
+
223
+ if ( ! $meta ) {
224
+ $meta = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
225
+ }
226
+
227
+ if ( is_wp_error( $meta ) ) {
228
+ return $paths;
229
+ }
230
+
231
+ $file_name = wp_basename( $file_path );
232
+
233
+ // If file edited, current file name might be different.
234
+ if ( isset( $meta['file'] ) ) {
235
+ $paths['file'] = str_replace( $file_name, wp_basename( $meta['file'] ), $file_path );
236
+ }
237
+
238
+ // Thumb
239
+ if ( isset( $meta['thumb'] ) ) {
240
+ $paths['thumb'] = str_replace( $file_name, $meta['thumb'], $file_path );
241
+ }
242
+
243
+ // Sizes
244
+ if ( isset( $meta['sizes'] ) ) {
245
+ foreach ( $meta['sizes'] as $size => $file ) {
246
+ if ( isset( $file['file'] ) ) {
247
+ $paths[ $size ] = str_replace( $file_name, $file['file'], $file_path );
248
+ }
249
+ }
250
+ }
251
+
252
+ $backups = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
253
+
254
+ // Backups
255
+ if ( $include_backups && is_array( $backups ) ) {
256
+ foreach ( $backups as $size => $file ) {
257
+ if ( isset( $file['file'] ) ) {
258
+ $paths[ $size ] = str_replace( $file_name, $file['file'], $file_path );
259
+ }
260
+ }
261
+ }
262
+
263
+ // Allow other processes to add files to be uploaded
264
+ $paths = apply_filters( 'as3cf_attachment_file_paths', $paths, $attachment_id, $meta );
265
+
266
+ // Remove duplicates
267
+ $paths = array_unique( $paths );
268
+
269
+ // Remove paths that don't exist
270
+ if ( $exists_locally ) {
271
+ foreach ( $paths as $key => $path ) {
272
+ if ( ! file_exists( $path ) ) {
273
+ unset( $paths[ $key ] );
274
+ }
275
+ }
276
+ }
277
+
278
+ return $paths;
279
+ }
280
+
281
+ /**
282
+ * Get an attachment's edited file paths.
283
+ *
284
+ * @param int $attachment_id
285
+ *
286
+ * @return array
287
+ */
288
+ public static function get_attachment_edited_file_paths( $attachment_id ) {
289
+ $paths = self::get_attachment_file_paths( $attachment_id, false );
290
+ $paths = array_filter( $paths, function ( $path ) {
291
+ return preg_match( '/-e[0-9]{13}(?:-[0-9]{1,4}x[0-9]{1,4})?\./', wp_basename( $path ) );
292
+ } );
293
+
294
+ return $paths;
295
+ }
296
+
297
+ /**
298
+ * Get an attachment's edited S3 keys.
299
+ *
300
+ * @param int $attachment_id
301
+ * @param array $s3object
302
+ *
303
+ * @return array
304
+ */
305
+ public static function get_attachment_edited_keys( $attachment_id, $s3object ) {
306
+ $prefix = trailingslashit( pathinfo( $s3object['key'], PATHINFO_DIRNAME ) );
307
+ $paths = self::get_attachment_edited_file_paths( $attachment_id );
308
+ $paths = array_map( function ( $path ) use ( $prefix ) {
309
+ return array( 'Key' => $prefix . wp_basename( $path ) );
310
+ }, $paths );
311
+
312
+ return $paths;
313
+ }
314
+
315
+ /**
316
+ * Get intermediate size from attachment filename.
317
+ *
318
+ * @param int $attachment_id
319
+ * @param string $filename
320
+ *
321
+ * @return string
322
+ */
323
+ public static function get_intermediate_size_from_filename( $attachment_id, $filename ) {
324
+ $sizes = self::get_attachment_file_paths( $attachment_id, false );
325
+
326
+ foreach ( $sizes as $size => $file ) {
327
+ if ( wp_basename( $file ) === $filename ) {
328
+ return $size;
329
+ }
330
+ }
331
+
332
+ return '';
333
+ }
334
+
335
+ /**
336
+ * Strip edited image suffix and extension from path.
337
+ *
338
+ * @param string $path
339
+ *
340
+ * @return string
341
+ */
342
+ public static function strip_image_edit_suffix_and_extension( $path ) {
343
+ $parts = pathinfo( $path );
344
+ $filename = preg_replace( '/-e[0-9]{13}/', '', $parts['filename'] );
345
+
346
+ return str_replace( $parts['basename'], $filename, $path );
347
+ }
348
+
349
+ /**
350
+ * Create a site link for given URL.
351
+ *
352
+ * @param string $url
353
+ * @param string $text
354
+ *
355
+ * @return string
356
+ */
357
+ public static function dbrains_link( $url, $text ) {
358
+ return sprintf( '<a href="%s">%s</a>', esc_url( $url ), esc_html( $text ) );
359
+ }
360
  }
361
+ }
classes/filters/as3cf-local-to-s3.php CHANGED
@@ -22,8 +22,8 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
22
  add_filter( 'excerpt_edit_pre', array( $this, 'filter_post' ) );
23
  add_filter( 'as3cf_filter_post_local_to_s3', array( $this, 'filter_post' ) );
24
  // Widgets
25
- add_filter( 'widget_text', array( $this, 'filter_widget' ) );
26
- add_filter( 'widget_form_callback', array( $this, 'filter_widget_form' ), 10, 2 );
27
  }
28
 
29
  /**
@@ -58,7 +58,7 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
58
  public function filter_post_data( $post ) {
59
  global $pages;
60
 
61
- $cache = $this->get_post_cache();
62
  $to_cache = array();
63
 
64
  if ( 1 === count( $pages ) && ! empty( $pages[0] ) ) {
@@ -94,42 +94,15 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
94
  }
95
 
96
  /**
97
- * Filter widget.
98
- *
99
- * @param string $content
100
- *
101
- * @return string
102
- */
103
- public function filter_widget( $content ) {
104
- $cache = $this->get_option_cache();
105
- $to_cache = array();
106
- $content = $this->process_content( $content, $cache, $to_cache );
107
-
108
- $this->maybe_update_option_cache( $to_cache );
109
-
110
- return $content;
111
- }
112
-
113
- /**
114
- * Filter widget form.
115
  *
116
  * @param array $instance
117
  * @param WP_Widget $class
118
  *
119
- * @return string
120
  */
121
- public function filter_widget_form( $instance, $class ) {
122
- if ( ! is_a( $class, 'WP_Widget_Text' ) || empty( $instance ) ) {
123
- return $instance;
124
- }
125
-
126
- $cache = $this->get_option_cache();
127
- $to_cache = array();
128
- $instance['text'] = $this->process_content( $instance['text'], $cache, $to_cache );
129
-
130
- $this->maybe_update_option_cache( $to_cache );
131
-
132
- return $instance;
133
  }
134
 
135
  /**
@@ -142,7 +115,7 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
142
  protected function url_needs_replacing( $url ) {
143
  $uploads = wp_upload_dir();
144
  $base_url = $this->as3cf->maybe_fix_local_subsite_url( $uploads['baseurl'] );
145
- $base_url = $this->as3cf->remove_scheme( $base_url );
146
 
147
  if ( false !== strpos( $url, $base_url ) ) {
148
  // Local URL, perform replacement
@@ -186,7 +159,7 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
186
  protected function get_attachment_id_from_url( $url ) {
187
  global $wpdb;
188
 
189
- $full_url = $this->as3cf->remove_scheme( $this->as3cf->remove_size_from_filename( $url ) );
190
 
191
  if ( isset( $this->query_cache[ $full_url ] ) ) {
192
  // ID already cached, return
@@ -194,7 +167,7 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
194
  }
195
 
196
  $upload_dir = wp_upload_dir();
197
- $base_url = $this->as3cf->remove_scheme( $upload_dir['baseurl'] );
198
  $path = $this->as3cf->decode_filename_in_path( ltrim( str_replace( $base_url, '', $full_url ), '/' ) );
199
 
200
  $sql = $wpdb->prepare( "
@@ -238,13 +211,13 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
238
  }
239
 
240
  $upload_dir = wp_upload_dir();
241
- $base_url = $this->as3cf->remove_scheme( $upload_dir['baseurl'] );
242
 
243
  $paths = array();
244
  $full_urls = array();
245
 
246
  foreach ( $urls as $url ) {
247
- $full_url = $this->as3cf->remove_scheme( $this->as3cf->remove_size_from_filename( $url ) );
248
 
249
  if ( isset( $this->query_cache[ $full_url ] ) ) {
250
  // ID already cached, use it.
@@ -335,7 +308,7 @@ class AS3CF_Local_To_S3 extends AS3CF_Filter {
335
  */
336
  protected function pre_replace_content( $content ) {
337
  $uploads = wp_upload_dir();
338
- $base_url = $this->as3cf->remove_scheme( $uploads['baseurl'] );
339
 
340
  return $this->remove_aws_query_strings( $content, $base_url );
341
  }
22
  add_filter( 'excerpt_edit_pre', array( $this, 'filter_post' ) );
23
  add_filter( 'as3cf_filter_post_local_to_s3', array( $this, 'filter_post' ) );
24
  // Widgets
25
+ add_filter( 'widget_form_callback', array( $this, 'filter_widget_display' ), 10, 2 );
26
+ add_filter( 'widget_display_callback', array( $this, 'filter_widget_display' ), 10, 2 );
27
  }
28
 
29
  /**
58
  public function filter_post_data( $post ) {
59
  global $pages;
60
 
61
+ $cache = $this->get_post_cache( $post->ID );
62
  $to_cache = array();
63
 
64
  if ( 1 === count( $pages ) && ! empty( $pages[0] ) ) {
94
  }
95
 
96
  /**
97
+ * Filter widget display.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  *
99
  * @param array $instance
100
  * @param WP_Widget $class
101
  *
102
+ * @return array
103
  */
104
+ public function filter_widget_display( $instance, $class ) {
105
+ return $this->handle_widget( $instance, $class );
 
 
 
 
 
 
 
 
 
 
106
  }
107
 
108
  /**
115
  protected function url_needs_replacing( $url ) {
116
  $uploads = wp_upload_dir();
117
  $base_url = $this->as3cf->maybe_fix_local_subsite_url( $uploads['baseurl'] );
118
+ $base_url = AS3CF_Utils::remove_scheme( $base_url );
119
 
120
  if ( false !== strpos( $url, $base_url ) ) {
121
  // Local URL, perform replacement
159
  protected function get_attachment_id_from_url( $url ) {
160
  global $wpdb;
161
 
162
+ $full_url = AS3CF_Utils::remove_scheme( AS3CF_Utils::remove_size_from_filename( $url ) );
163
 
164
  if ( isset( $this->query_cache[ $full_url ] ) ) {
165
  // ID already cached, return
167
  }
168
 
169
  $upload_dir = wp_upload_dir();
170
+ $base_url = AS3CF_Utils::remove_scheme( $upload_dir['baseurl'] );
171
  $path = $this->as3cf->decode_filename_in_path( ltrim( str_replace( $base_url, '', $full_url ), '/' ) );
172
 
173
  $sql = $wpdb->prepare( "
211
  }
212
 
213
  $upload_dir = wp_upload_dir();
214
+ $base_url = AS3CF_Utils::remove_scheme( $upload_dir['baseurl'] );
215
 
216
  $paths = array();
217
  $full_urls = array();
218
 
219
  foreach ( $urls as $url ) {
220
+ $full_url = AS3CF_Utils::remove_scheme( AS3CF_Utils::remove_size_from_filename( $url ) );
221
 
222
  if ( isset( $this->query_cache[ $full_url ] ) ) {
223
  // ID already cached, use it.
308
  */
309
  protected function pre_replace_content( $content ) {
310
  $uploads = wp_upload_dir();
311
+ $base_url = AS3CF_Utils::remove_scheme( $uploads['baseurl'] );
312
 
313
  return $this->remove_aws_query_strings( $content, $base_url );
314
  }
classes/filters/as3cf-s3-to-local.php CHANGED
@@ -18,7 +18,7 @@ class AS3CF_S3_To_Local extends AS3CF_Filter {
18
  add_filter( 'excerpt_save_pre', array( $this, 'filter_post' ) );
19
  add_filter( 'as3cf_filter_post_s3_to_local', array( $this, 'filter_post' ) );
20
  // Widgets
21
- add_filter( 'widget_update_callback', array( $this, 'filter_widget_update' ), 10, 4 );
22
  }
23
 
24
  /**
@@ -36,7 +36,7 @@ class AS3CF_S3_To_Local extends AS3CF_Filter {
36
  }
37
 
38
  /**
39
- * Filter widget update.
40
  *
41
  * @param array $instance
42
  * @param array $new_instance
@@ -46,24 +46,8 @@ class AS3CF_S3_To_Local extends AS3CF_Filter {
46
  * @return array
47
  *
48
  */
49
- public function filter_widget_update( $instance, $new_instance, $old_instance, $class ) {
50
- if ( ! is_a( $class, 'WP_Widget_Text' ) || empty( $instance ) ) {
51
- return $instance;
52
- }
53
-
54
- $cache = $this->get_option_cache();
55
- $to_cache = array();
56
- $instance['text'] = $this->process_content( $instance['text'], $cache, $to_cache );
57
-
58
- // Editing Text Widget in Customizer throws an error if more than one option record is updated.
59
- // Therefore cache updating has to wait until render or edit via Appearance menu.
60
- if ( isset( $_POST['wp_customize'] ) && 'on' === $_POST['wp_customize'] ) {
61
- return $instance;
62
- }
63
-
64
- $this->maybe_update_option_cache( $to_cache );
65
-
66
- return $instance;
67
  }
68
 
69
  /**
@@ -84,7 +68,7 @@ class AS3CF_S3_To_Local extends AS3CF_Filter {
84
  */
85
  protected function url_needs_replacing( $url ) {
86
  $uploads = wp_upload_dir();
87
- $base_url = $this->as3cf->remove_scheme( $uploads['baseurl'] );
88
 
89
  if ( false !== strpos( $url, $base_url ) ) {
90
  // Local URL, no replacement needed
@@ -128,14 +112,14 @@ class AS3CF_S3_To_Local extends AS3CF_Filter {
128
  protected function get_attachment_id_from_url( $url ) {
129
  global $wpdb;
130
 
131
- $full_url = $this->as3cf->remove_size_from_filename( $url );
132
 
133
  if ( isset( $this->query_cache[ $full_url ] ) ) {
134
  // ID already cached, return
135
  return $this->query_cache[ $full_url ];
136
  }
137
 
138
- $parts = parse_url( $full_url );
139
  $path = $this->as3cf->decode_filename_in_path( ltrim( $parts['path'], '/' ) );
140
 
141
  if ( false !== strpos( $path, '/' ) ) {
18
  add_filter( 'excerpt_save_pre', array( $this, 'filter_post' ) );
19
  add_filter( 'as3cf_filter_post_s3_to_local', array( $this, 'filter_post' ) );
20
  // Widgets
21
+ add_filter( 'widget_update_callback', array( $this, 'filter_widget_save' ), 10, 4 );
22
  }
23
 
24
  /**
36
  }
37
 
38
  /**
39
+ * Filter widget on save.
40
  *
41
  * @param array $instance
42
  * @param array $new_instance
46
  * @return array
47
  *
48
  */
49
+ public function filter_widget_save( $instance, $new_instance, $old_instance, $class ) {
50
+ return $this->handle_widget( $instance, $class );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
  /**
68
  */
69
  protected function url_needs_replacing( $url ) {
70
  $uploads = wp_upload_dir();
71
+ $base_url = AS3CF_Utils::remove_scheme( $uploads['baseurl'] );
72
 
73
  if ( false !== strpos( $url, $base_url ) ) {
74
  // Local URL, no replacement needed
112
  protected function get_attachment_id_from_url( $url ) {
113
  global $wpdb;
114
 
115
+ $full_url = AS3CF_Utils::remove_size_from_filename( $url );
116
 
117
  if ( isset( $this->query_cache[ $full_url ] ) ) {
118
  // ID already cached, return
119
  return $this->query_cache[ $full_url ];
120
  }
121
 
122
+ $parts = AS3CF_Utils::parse_url( $full_url );
123
  $path = $this->as3cf->decode_filename_in_path( ltrim( $parts['path'], '/' ) );
124
 
125
  if ( false !== strpos( $path, '/' ) ) {
classes/upgrades/exceptions/batch-limits-exceeded-exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions;
4
+
5
+ class Batch_Limits_Exceeded_Exception extends \Exception {
6
+
7
+ }
classes/upgrades/exceptions/no-more-blogs-exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions;
4
+
5
+ class No_More_Blogs_Exception extends \Exception {
6
+
7
+ }
classes/upgrades/exceptions/too-many-errors-exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions;
4
+
5
+ class Too_Many_Errors_Exception extends \Exception {
6
+
7
+ }
classes/upgrades/network-upgrade.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
4
+
5
+ use Amazon_S3_And_CloudFront;
6
+
7
+ abstract class Network_Upgrade {
8
+
9
+ /**
10
+ * @var Amazon_S3_And_CloudFront
11
+ */
12
+ protected $as3cf;
13
+
14
+ /**
15
+ * @var string
16
+ */
17
+ protected $version;
18
+
19
+ /**
20
+ * Network_Upgrade constructor.
21
+ *
22
+ * @param Amazon_S3_And_CloudFront $as3cf
23
+ * @param string $version
24
+ */
25
+ public function __construct( $as3cf, $version ) {
26
+ $this->as3cf = $as3cf;
27
+ $this->version = $version;
28
+
29
+ add_action( 'admin_init', array( $this, 'init' ) );
30
+ }
31
+
32
+ /**
33
+ * Init upgrade.
34
+ */
35
+ public function init() {
36
+ if ( ! $this->maybe_upgrade() ) {
37
+ return;
38
+ }
39
+
40
+ $this->do_upgrade();
41
+ $this->save_version();
42
+ }
43
+
44
+ /**
45
+ * Maybe perform upgrade?
46
+ *
47
+ * @return bool
48
+ */
49
+ protected function maybe_upgrade() {
50
+ $stored_version = $this->get_stored_version();
51
+
52
+ if ( version_compare( $stored_version, $this->version, '<' ) ) {
53
+ return true;
54
+ }
55
+
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * Get stored version.
61
+ *
62
+ * @return string
63
+ */
64
+ protected function get_stored_version() {
65
+ static $version;
66
+
67
+ if ( is_null( $version ) ) {
68
+ $version = get_site_option( $this->get_option_name(), '0.0' );
69
+ }
70
+
71
+ return $version;
72
+ }
73
+
74
+ /**
75
+ * Get option name.
76
+ *
77
+ * @return string
78
+ */
79
+ protected function get_option_name() {
80
+ return strtolower( get_class( $this->as3cf ) ) . '_version';
81
+ }
82
+
83
+ /**
84
+ * Save version to options table.
85
+ */
86
+ protected function save_version() {
87
+ update_site_option( $this->get_option_name(), $this->version );
88
+ }
89
+
90
+ /**
91
+ * Perform upgrade logic.
92
+ */
93
+ abstract protected function do_upgrade();
94
+
95
+ }
classes/upgrades/{as3cf-filter-post-content.php → upgrade-content-replace-urls.php} RENAMED
@@ -1,19 +1,16 @@
1
  <?php
2
 
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
 
8
  /**
9
- * AS3CF_Upgrade_Content_Replace_URLs Class
10
  *
11
  * This class handles replacing all S3 URLs in post
12
  * content with the local URL.
13
  *
14
  * @since 1.2
15
  */
16
- class AS3CF_Upgrade_Content_Replace_URLs extends AS3CF_Upgrade_Filter_Post {
17
 
18
  /**
19
  * @var int
@@ -49,23 +46,23 @@ class AS3CF_Upgrade_Content_Replace_URLs extends AS3CF_Upgrade_Filter_Post {
49
  }
50
 
51
  /**
52
- * Process blog.
53
  *
54
- * @param array $blog
55
  */
56
- protected function process_blog( $blog ) {
57
- $this->upgrade_theme_mods( $blog['prefix'] );
 
 
58
  }
59
 
60
  /**
61
  * Upgrade theme mods. Ensures background and header images have local URLs saved to the database.
62
- *
63
- * @param string $prefix
64
  */
65
- protected function upgrade_theme_mods( $prefix ) {
66
  global $wpdb;
67
 
68
- $mods = $wpdb->get_results( "SELECT * FROM `{$prefix}options` WHERE option_name LIKE 'theme_mods_%'" );
69
 
70
  foreach ( $mods as $mod ) {
71
  $value = maybe_unserialize( $mod->option_value );
@@ -85,7 +82,7 @@ class AS3CF_Upgrade_Content_Replace_URLs extends AS3CF_Upgrade_Filter_Post {
85
  $value = maybe_serialize( $value );
86
 
87
  if ( $value !== $mod->option_value ) {
88
- $wpdb->query( "UPDATE `{$prefix}options` SET option_value = '{$value}' WHERE option_id = '{$mod->option_id}'" );
89
  }
90
  }
91
  }
1
  <?php
2
 
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
 
 
 
4
 
5
  /**
6
+ * Upgrade_Content_Replace_URLs Class
7
  *
8
  * This class handles replacing all S3 URLs in post
9
  * content with the local URL.
10
  *
11
  * @since 1.2
12
  */
13
+ class Upgrade_Content_Replace_URLs extends Upgrade_Filter_Post {
14
 
15
  /**
16
  * @var int
46
  }
47
 
48
  /**
49
+ * Switch to a new blog for processing.
50
  *
51
+ * @return bool
52
  */
53
+ protected function upgrade_blog() {
54
+ $this->upgrade_theme_mods();
55
+
56
+ return parent::upgrade_blog();
57
  }
58
 
59
  /**
60
  * Upgrade theme mods. Ensures background and header images have local URLs saved to the database.
 
 
61
  */
62
+ protected function upgrade_theme_mods() {
63
  global $wpdb;
64
 
65
+ $mods = $wpdb->get_results( "SELECT * FROM `{$wpdb->prefix}options` WHERE option_name LIKE 'theme_mods_%'" );
66
 
67
  foreach ( $mods as $mod ) {
68
  $value = maybe_unserialize( $mod->option_value );
82
  $value = maybe_serialize( $value );
83
 
84
  if ( $value !== $mod->option_value ) {
85
+ $wpdb->query( "UPDATE `{$wpdb->prefix}options` SET option_value = '{$value}' WHERE option_id = '{$mod->option_id}'" );
86
  }
87
  }
88
  }
classes/upgrades/{as3cf-filter-edd.php → upgrade-edd-replace-urls.php} RENAMED
@@ -1,19 +1,16 @@
1
  <?php
2
 
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
 
8
  /**
9
- * AS3CF_Upgrade_EDD_Replace_URLs Class
10
  *
11
  * This class handles replacing all S3 URLs in EDD
12
  * downloads with the local URL.
13
  *
14
  * @since 1.2
15
  */
16
- class AS3CF_Upgrade_EDD_Replace_URLs extends AS3CF_Upgrade {
17
 
18
  /**
19
  * @var int
@@ -39,24 +36,6 @@ class AS3CF_Upgrade_EDD_Replace_URLs extends AS3CF_Upgrade {
39
  return __( 'and ensuring that only the local URL exists in EDD post meta.', 'amazon-s3-and-cloudfront' );
40
  }
41
 
42
- /**
43
- * Count attachments to process.
44
- *
45
- * @return int
46
- */
47
- protected function count_items_to_process() {
48
- global $wpdb;
49
-
50
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
51
- $count = 0;
52
-
53
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
54
- $count += (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table_prefix}postmeta` WHERE meta_key = 'edd_download_files'" );
55
- }
56
-
57
- return $count;
58
- }
59
-
60
  /**
61
  * Get items to process.
62
  *
@@ -75,7 +54,9 @@ class AS3CF_Upgrade_EDD_Replace_URLs extends AS3CF_Upgrade {
75
  $sql .= " AND meta_id > {$offset->meta_id}";
76
  }
77
 
78
- $sql .= " LIMIT {$limit}";
 
 
79
 
80
  return $wpdb->get_results( $sql );
81
  }
@@ -107,8 +88,8 @@ class AS3CF_Upgrade_EDD_Replace_URLs extends AS3CF_Upgrade {
107
  }
108
 
109
  update_post_meta( $item->post_id, 'edd_download_files', $attachments );
110
-
111
  return true;
112
  }
113
-
114
  }
1
  <?php
2
 
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
 
 
 
4
 
5
  /**
6
+ * Upgrade_EDD_Replace_URLs Class
7
  *
8
  * This class handles replacing all S3 URLs in EDD
9
  * downloads with the local URL.
10
  *
11
  * @since 1.2
12
  */
13
+ class Upgrade_EDD_Replace_URLs extends Upgrade {
14
 
15
  /**
16
  * @var int
36
  return __( 'and ensuring that only the local URL exists in EDD post meta.', 'amazon-s3-and-cloudfront' );
37
  }
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  /**
40
  * Get items to process.
41
  *
54
  $sql .= " AND meta_id > {$offset->meta_id}";
55
  }
56
 
57
+ if ( $limit && $limit > 0 ) {
58
+ $sql .= sprintf( ' LIMIT %d', (int) $limit );
59
+ }
60
 
61
  return $wpdb->get_results( $sql );
62
  }
88
  }
89
 
90
  update_post_meta( $item->post_id, 'edd_download_files', $attachments );
91
+
92
  return true;
93
  }
94
+
95
  }
classes/upgrades/{as3cf-file-sizes.php → upgrade-file-sizes.php} RENAMED
@@ -9,20 +9,20 @@
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
  * @var int
@@ -72,11 +72,11 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
72
  return false;
73
  }
74
 
75
- $s3client = $this->as3cf->get_s3client( $region, true );
76
- $main_file = $s3object['key'];
77
 
78
- $ext = pathinfo( $main_file, PATHINFO_EXTENSION );
79
- $prefix = trailingslashit( dirname( $s3object['key'] ) );
80
 
81
  // Used to search S3 for all files related to an attachment
82
  $search_prefix = $prefix . wp_basename( $main_file, ".$ext" );
@@ -91,15 +91,15 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
91
  $result = $s3client->ListObjects( $args );
92
  } catch ( Exception $e ) {
93
  AS3CF_Error::log( 'Error listing objects of prefix ' . $search_prefix . ' for attachment ' . $attachment->ID . ' from S3: ' . $e->getMessage() );
94
- $this->error_count ++;
95
 
96
  return false;
97
  }
98
 
99
  $file_size_total = 0;
100
- $main_file_size = 0;
101
 
102
- foreach ( $result->get( 'Contents' ) as $object ) {
103
  if ( ! isset( $object['Size'] ) ) {
104
  continue;
105
  }
@@ -117,7 +117,7 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
117
 
118
  if ( 0 === $file_size_total ) {
119
  AS3CF_Error::log( 'Total file size for the attachment is 0: ' . $attachment->ID );
120
- $this->error_count ++;
121
 
122
  return false;
123
  }
@@ -134,26 +134,7 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
134
  }
135
 
136
  /**
137
- * Get a count of all attachments without region in their S3 metadata
138
- * for the whole site
139
- *
140
- * @return int
141
- */
142
- protected function count_items_to_process() {
143
- // get the table prefixes for all the blogs
144
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
145
- $all_count = 0;
146
-
147
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
148
- $count = $this->get_attachments_removed_from_server( $table_prefix, true );
149
- $all_count += $count;
150
- }
151
-
152
- return $all_count;
153
- }
154
-
155
- /**
156
- * Get all attachments that don't have region in their S3 meta data for a blog
157
  *
158
  * @param string $prefix
159
  * @param int $limit
@@ -162,7 +143,14 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
162
  * @return array
163
  */
164
  protected function get_items_to_process( $prefix, $limit, $offset = false ) {
165
- $attachments = $this->get_attachments_removed_from_server( $prefix, false, $limit );
 
 
 
 
 
 
 
166
 
167
  return $attachments;
168
  }
@@ -187,39 +175,10 @@ class AS3CF_Upgrade_File_Sizes extends AS3CF_Upgrade {
187
  WHERE pm1.`meta_key` = 'amazonS3_info'
188
  AND pm2.`post_id` is null";
189
 
190
- if ( ! is_null( $limit ) ) {
191
- $sql .= ' LIMIT %d';
192
-
193
- $sql = $wpdb->prepare( $sql, $limit );
194
  }
195
 
196
  return $wpdb->get_results( $sql, OBJECT );
197
  }
198
-
199
- /**
200
- * Get S3 attachments that have had their local file removed from the server
201
- *
202
- * @param string $prefix
203
- * @param bool|false $count
204
- * @param null|int $limit
205
- *
206
- * @return array|int
207
- */
208
- protected function get_attachments_removed_from_server( $prefix, $count = false, $limit = null ) {
209
- $all_attachments = $this->get_s3_attachments( $prefix, $limit );
210
- $attachments = array();
211
-
212
- foreach ( $all_attachments as $attachment ) {
213
- if ( ! file_exists( get_attached_file( $attachment->ID, true ) ) ) {
214
- $attachments[] = $attachment;
215
- }
216
- }
217
-
218
- if ( $count ) {
219
- return count( $attachments );
220
- }
221
-
222
- return $attachments;
223
- }
224
-
225
  }
9
  * @since 0.9.3
10
  */
11
 
12
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
13
+
14
+ use AS3CF_Error;
15
+ use Exception;
16
 
17
  /**
18
+ * 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 Upgrade_File_Sizes extends Upgrade {
26
 
27
  /**
28
  * @var int
72
  return false;
73
  }
74
 
75
+ $s3client = $this->as3cf->get_s3client( $region, true );
76
+ $main_file = $s3object['key'];
77
 
78
+ $ext = pathinfo( $main_file, PATHINFO_EXTENSION );
79
+ $prefix = trailingslashit( dirname( $s3object['key'] ) );
80
 
81
  // Used to search S3 for all files related to an attachment
82
  $search_prefix = $prefix . wp_basename( $main_file, ".$ext" );
91
  $result = $s3client->ListObjects( $args );
92
  } catch ( Exception $e ) {
93
  AS3CF_Error::log( 'Error listing objects of prefix ' . $search_prefix . ' for attachment ' . $attachment->ID . ' from S3: ' . $e->getMessage() );
94
+ $this->error_count++;
95
 
96
  return false;
97
  }
98
 
99
  $file_size_total = 0;
100
+ $main_file_size = 0;
101
 
102
+ foreach ( (array) $result->get( 'Contents' ) as $object ) {
103
  if ( ! isset( $object['Size'] ) ) {
104
  continue;
105
  }
117
 
118
  if ( 0 === $file_size_total ) {
119
  AS3CF_Error::log( 'Total file size for the attachment is 0: ' . $attachment->ID );
120
+ $this->error_count++;
121
 
122
  return false;
123
  }
134
  }
135
 
136
  /**
137
+ * Get all attachments removed from the server.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  *
139
  * @param string $prefix
140
  * @param int $limit
143
  * @return array
144
  */
145
  protected function get_items_to_process( $prefix, $limit, $offset = false ) {
146
+ $all_attachments = $this->get_s3_attachments( $prefix, $limit );
147
+ $attachments = array();
148
+
149
+ foreach ( $all_attachments as $attachment ) {
150
+ if ( ! file_exists( get_attached_file( $attachment->ID, true ) ) ) {
151
+ $attachments[] = $attachment;
152
+ }
153
+ }
154
 
155
  return $attachments;
156
  }
175
  WHERE pm1.`meta_key` = 'amazonS3_info'
176
  AND pm2.`post_id` is null";
177
 
178
+ if ( $limit && $limit > 0 ) {
179
+ $sql .= sprintf( ' LIMIT %d', (int) $limit );
 
 
180
  }
181
 
182
  return $wpdb->get_results( $sql, OBJECT );
183
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  }
classes/upgrades/{as3cf-filter-post-excerpt.php → upgrade-filter-post-excerpt.php} RENAMED
@@ -1,19 +1,16 @@
1
  <?php
2
 
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
 
8
  /**
9
- * AS3CF_Upgrade_Filter_Post_Excerpt Class
10
  *
11
  * This class handles replacing all S3 URLs in post
12
  * excerpts with the local URL.
13
  *
14
  * @since 1.3
15
  */
16
- class AS3CF_Upgrade_Filter_Post_Excerpt extends AS3CF_Upgrade_Filter_Post {
17
 
18
  /**
19
  * @var int
1
  <?php
2
 
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
 
 
 
4
 
5
  /**
6
+ * Upgrade_Filter_Post_Excerpt Class
7
  *
8
  * This class handles replacing all S3 URLs in post
9
  * excerpts with the local URL.
10
  *
11
  * @since 1.3
12
  */
13
+ class Upgrade_Filter_Post_Excerpt extends Upgrade_Filter_Post {
14
 
15
  /**
16
  * @var int
classes/{as3cf-upgrade-filter-post.php → upgrades/upgrade-filter-post.php} RENAMED
@@ -1,39 +1,35 @@
1
  <?php
2
 
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
 
7
 
8
  /**
9
- * AS3CF_Upgrade_Filter Class
10
  *
11
  * The base upgrade class for handling find and replace
12
  * on the posts tables for content filtering.
13
  *
14
  * @since 1.3
15
  */
16
- abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
17
 
18
  /**
19
- * @var string 'metadata', 'attachment'
20
  */
21
- protected $upgrade_type = 'posts';
22
 
23
  /**
24
- * @var int Current blog ID
25
  */
26
- protected $blog_id;
27
 
28
  /**
29
- * @var int Finish time
30
- */
31
- protected $finish;
32
-
33
- /**
34
- * @var array Session data
35
  */
36
- protected $session;
37
 
38
  /**
39
  * @var string
@@ -41,209 +37,19 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
41
  protected $column_name;
42
 
43
  /**
44
- * Fire up the upgrade
45
- */
46
- protected function init() {
47
- $session = array(
48
- 'status' => self::STATUS_RUNNING,
49
- 'total_attachments' => 0,
50
- 'processed_attachments' => 0,
51
- 'blogs_processed' => false,
52
- 'blogs' => array(),
53
- );
54
-
55
- foreach ( $this->as3cf->get_all_blog_table_prefixes() as $blog_id => $prefix ) {
56
- $session['blogs'][ $blog_id ] = array(
57
- 'prefix' => $prefix,
58
- 'processed' => false,
59
- 'total_attachments' => null,
60
- 'last_attachment_id' => null,
61
- 'highest_post_id' => null,
62
- 'last_post_id' => null,
63
- );
64
- }
65
-
66
- $this->save_session( $session );
67
- $this->schedule();
68
- }
69
-
70
- /**
71
- * Count attachments to process. We don't care about the total at this stage
72
- * so just loop over blogs until attachments exist on S3.
73
- *
74
- * @return int
75
  */
76
- protected function count_items_to_process() {
77
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
78
-
79
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
80
- if ( $this->as3cf->count_attachments( $table_prefix, true ) ) {
81
- return 1;
82
- }
83
- }
84
-
85
- return 0;
86
- }
87
-
88
- /**
89
- * Cron job to update post content, ensuring no S3 URLs exist.
90
- */
91
- public function do_upgrade() {
92
- $this->lock_upgrade();
93
-
94
- // Check if the cron should even be running
95
- if ( $this->get_saved_upgrade_id() >= $this->upgrade_id || $this->get_upgrade_status() !== self::STATUS_RUNNING ) {
96
- $this->unschedule();
97
-
98
- return;
99
- }
100
-
101
- $limit = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_batch_size', 50 );
102
- $this->finish = time() + apply_filters( 'as3cf_update_' . $this->upgrade_name . '_time_limit', 10 );
103
- $this->session = $this->get_session();
104
-
105
- if ( ! $this->maybe_process_blogs() ) {
106
- // Blogs still to process but limits reached, return
107
- $this->save_session( $this->session );
108
-
109
- return;
110
- }
111
-
112
- foreach ( $this->session['blogs'] as $blog_id => $blog ) {
113
- $this->blog_id = $blog_id;
114
- $this->as3cf->switch_to_blog( $blog_id );
115
-
116
- if ( $this->batch_limit_reached() ) {
117
- // Limits reached, end batch
118
- break;
119
- }
120
-
121
- if ( $blog['processed'] ) {
122
- // Blog processed, move onto the next
123
- continue;
124
- }
125
-
126
- $offset = $this->session['blogs'][ $blog_id ]['last_attachment_id'];
127
- $attachments = $this->get_items_to_process( $blog['prefix'], $limit, $offset );
128
-
129
- if ( empty( $attachments ) ) {
130
- // All attachments processed, maybe move onto next blog
131
- $this->session['blogs'][ $blog_id ]['processed'] = true;
132
-
133
- if ( $this->all_blogs_processed() ) {
134
- // All blogs processed, complete upgrade
135
- $this->upgrade_finished();
136
-
137
- return;
138
- }
139
-
140
- continue;
141
- }
142
-
143
- foreach ( $attachments as $attachment ) {
144
- if ( $this->batch_limit_reached() ) {
145
- // Limits reached, end batch
146
- break 2;
147
- }
148
-
149
- if ( $this->upgrade_item( $attachment ) ) {
150
- $this->session['processed_attachments'] += 1;
151
- $this->session['blogs'][ $blog_id ]['last_attachment_id'] = $attachment->ID;
152
- $this->session['blogs'][ $blog_id ]['last_post_id'] = null;
153
- } else {
154
- // Limits reached while processing posts, end batch
155
- break 2;
156
- }
157
- }
158
-
159
- $this->as3cf->restore_current_blog();
160
- }
161
-
162
- $this->maybe_finish_upgrade();
163
- }
164
-
165
- /**
166
- * Maybe finish the upgrade process.
167
- */
168
- protected function maybe_finish_upgrade() {
169
- if ( $this->session['processed_attachments'] >= $this->session['total_attachments']) {
170
- $this->upgrade_finished();
171
-
172
- return;
173
- }
174
-
175
- $this->save_session( $this->session );
176
- }
177
-
178
- /**
179
- * Maybe process blogs.
180
- *
181
- * @return bool
182
- */
183
- protected function maybe_process_blogs() {
184
- if ( $this->session['blogs_processed'] ) {
185
- // Blogs already processed, return
186
- return true;
187
- }
188
-
189
- foreach ( $this->session['blogs'] as $blog_id => $blog ) {
190
- if ( $this->batch_limit_reached() ) {
191
- // Limits reached, return
192
- return false;
193
- }
194
-
195
- if ( is_null( $blog['total_attachments'] ) ) {
196
- if ( method_exists( $this, 'process_blog' ) ) {
197
- $this->process_blog( $blog );
198
- }
199
-
200
- // Count total attachments
201
- $count = $this->as3cf->count_attachments( $blog['prefix'], true );
202
-
203
- // Update blog session data
204
- $this->session['blogs'][ $blog_id ]['total_attachments'] = $count;
205
- $this->session['total_attachments'] += $count;
206
- }
207
-
208
- if ( is_null( $blog['highest_post_id'] ) ) {
209
- // Retrieve highest post ID
210
- $this->session['blogs'][ $blog_id ]['highest_post_id'] = $this->get_highest_post_id( $blog['prefix'] );
211
- }
212
- }
213
-
214
- $this->session['blogs_processed'] = true;
215
-
216
- return true;
217
- }
218
 
219
  /**
220
  * Get highest post ID.
221
  *
222
- * @param string $prefix
223
- *
224
  * @return int
225
  */
226
- protected function get_highest_post_id( $prefix ) {
227
  global $wpdb;
228
 
229
- $sql = "SELECT ID FROM `{$prefix}posts` ORDER BY ID DESC LIMIT 1";
230
-
231
- return (int) $wpdb->get_var( $sql );
232
- }
233
-
234
- /**
235
- * All blogs processed.
236
- *
237
- * @return bool
238
- */
239
- protected function all_blogs_processed() {
240
- foreach ( $this->session['blogs'] as $blog ) {
241
- if ( ! $blog['processed'] ) {
242
- return false;
243
- }
244
- }
245
-
246
- return true;
247
  }
248
 
249
  /**
@@ -268,33 +74,59 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
268
  $sql .= " AND posts.ID < '{$offset}'";
269
  }
270
 
271
- $sql .= " ORDER BY posts.ID DESC LIMIT {$limit}";
 
 
 
 
272
 
273
  return $wpdb->get_results( $sql );
274
  }
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  /**
277
  * Upgrade attachment.
278
  *
 
 
 
279
  * @param mixed $attachment
280
  *
281
  * @return bool
282
  */
283
  protected function upgrade_item( $attachment ) {
284
  $limit = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_sql_limit', 100000 );
285
- $highest_post_id = $this->session['blogs'][ $this->blog_id ]['highest_post_id'];
286
- $last_post_id = $this->session['blogs'][ $this->blog_id ]['last_post_id'];
287
- $where_highest_id = is_null( $last_post_id ) ? $highest_post_id : $last_post_id;
288
  $where_lowest_id = max( $where_highest_id - $limit, 0 );
289
 
290
  while ( true ) {
291
  $this->find_and_replace_attachment_urls( $attachment->ID, $where_lowest_id, $where_highest_id );
292
 
293
- if ( $this->batch_limit_reached() ) {
294
- // Batch limit reached
295
- break;
296
- }
297
-
298
  if ( $where_lowest_id <= 0 ) {
299
  // Batch completed
300
  return true;
@@ -302,13 +134,25 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
302
 
303
  $where_highest_id = $where_lowest_id;
304
  $where_lowest_id = max( $where_lowest_id - $limit, 0 );
 
 
305
  }
306
 
307
- $this->session['blogs'][ $this->blog_id ]['last_post_id'] = $where_lowest_id;
308
 
309
  return false;
310
  }
311
 
 
 
 
 
 
 
 
 
 
 
312
  /**
313
  * Find and replace embedded URLs for an attachment.
314
  *
@@ -385,8 +229,8 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
385
 
386
  // Remove URL protocols
387
  foreach ( $url_pairs as $key => $url_pair ) {
388
- $url_pairs[ $key ]['old_url'] = $this->as3cf->remove_scheme( $url_pair['old_url'] );
389
- $url_pairs[ $key ]['new_url'] = $this->as3cf->remove_scheme( $url_pair['new_url'] );
390
  }
391
 
392
  return apply_filters( 'as3cf_update_' . $this->upgrade_name . '_url_pairs', $url_pairs, $file_path, $old_url, $new_url, $meta );
@@ -486,7 +330,7 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
486
 
487
  // Get unique URLs without size string and extension
488
  foreach ( $url_pairs as $url_pair ) {
489
- $paths[] = $this->as3cf->remove_size_from_filename( $url_pair['old_url'], true );
490
  }
491
 
492
  $paths = array_unique( $paths );
@@ -551,42 +395,62 @@ abstract class AS3CF_Upgrade_Filter_Post extends AS3CF_Upgrade {
551
  */
552
  protected function get_generic_message() {
553
  $link_text = __( 'See our documentation', 'amazon-s3-and-cloudfront' );
554
- $link = $this->as3cf->dbrains_link( 'https://deliciousbrains.com/wp-offload-s3/doc/version-1-2-upgrade', $link_text );
 
 
 
555
 
556
  return sprintf( __( '%s for details on why we&#8217;re doing this, why it runs slowly, and how to make it run faster.', 'amazon-s3-and-cloudfront' ), $link );
557
  }
558
 
559
  /**
560
- * Calculate progress.
561
  *
562
- * @return bool|int|float
 
 
 
563
  */
564
- protected function calculate_progress() {
565
- $session = $this->get_session();
 
 
 
 
 
 
 
566
 
567
- if ( ! isset( $session['total_attachments'] ) || ! isset( $session['processed_attachments'] ) ) {
568
- // Session data not created, return
569
- return false;
 
 
 
 
 
570
  }
571
 
572
- if ( ! $session['blogs_processed'] || is_null( $session['total_attachments'] ) || is_null( $session['processed_attachments'] ) ) {
573
- // Still processing blogs, return 0
574
- return 0;
575
  }
576
 
577
- return round( $session['processed_attachments'] / $session['total_attachments'] * 100, 2 );
578
  }
579
 
580
  /**
581
- * Batch limit reached.
582
  *
583
- * @return bool
 
 
 
584
  */
585
- protected function batch_limit_reached() {
586
- if ( time() >= $this->finish || $this->as3cf->memory_exceeded( 'as3cf_update_' . $this->upgrade_name . '_memory_exceeded' ) ) {
587
- return true;
588
  }
589
 
590
- return false;
591
  }
592
  }
1
  <?php
2
 
3
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
4
+
5
+ use AS3CF_Utils;
6
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions\Batch_Limits_Exceeded_Exception;
7
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions\Too_Many_Errors_Exception;
8
 
9
  /**
10
+ * Upgrade_Filter_Post Class
11
  *
12
  * The base upgrade class for handling find and replace
13
  * on the posts tables for content filtering.
14
  *
15
  * @since 1.3
16
  */
17
+ abstract class Upgrade_Filter_Post extends Upgrade {
18
 
19
  /**
20
+ * @var int Time limit in seconds.
21
  */
22
+ protected $time_limit = 10;
23
 
24
  /**
25
+ * @var int Batch size limit for this request session.
26
  */
27
+ protected $size_limit = 50;
28
 
29
  /**
30
+ * @var string 'metadata', 'attachment'
 
 
 
 
 
31
  */
32
+ protected $upgrade_type = 'posts';
33
 
34
  /**
35
  * @var string
37
  protected $column_name;
38
 
39
  /**
40
+ * @var int The last post ID used for the bottom range of the item upgrade.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  */
42
+ protected $last_post_id;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  /**
45
  * Get highest post ID.
46
  *
 
 
47
  * @return int
48
  */
49
+ protected function get_highest_post_id() {
50
  global $wpdb;
51
 
52
+ return (int) $wpdb->get_var( "SELECT MAX(ID) FROM {$wpdb->prefix}posts" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
 
55
  /**
74
  $sql .= " AND posts.ID < '{$offset}'";
75
  }
76
 
77
+ $sql .= " ORDER BY posts.ID DESC";
78
+
79
+ if ( $limit && $limit > 0 ) {
80
+ $sql .= sprintf( ' LIMIT %d', (int) $limit );
81
+ }
82
 
83
  return $wpdb->get_results( $sql );
84
  }
85
 
86
+ /**
87
+ * Switch to the given blog, and update blog-specific state.
88
+ *
89
+ * @param $blog_id
90
+ */
91
+ protected function switch_to_blog( $blog_id ) {
92
+ parent::switch_to_blog( $blog_id );
93
+ $this->last_post_id = $this->load_last_post_id();
94
+ }
95
+
96
+ /**
97
+ * Mark the current blog upgrade as complete.
98
+ */
99
+ protected function blog_upgrade_completed() {
100
+ parent::blog_upgrade_completed();
101
+ $this->last_post_id = null;
102
+ }
103
+
104
+ /**
105
+ * Prepare the session to be persisted.
106
+ */
107
+ protected function close_session() {
108
+ parent::close_session();
109
+ $this->session['last_post_id'] = $this->last_post_id;
110
+ }
111
+
112
  /**
113
  * Upgrade attachment.
114
  *
115
+ * @throws Batch_Limits_Exceeded_Exception
116
+ * @throws Too_Many_Errors_Exception
117
+ *
118
  * @param mixed $attachment
119
  *
120
  * @return bool
121
  */
122
  protected function upgrade_item( $attachment ) {
123
  $limit = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_sql_limit', 100000 );
124
+ $where_highest_id = $this->last_post_id;
 
 
125
  $where_lowest_id = max( $where_highest_id - $limit, 0 );
126
 
127
  while ( true ) {
128
  $this->find_and_replace_attachment_urls( $attachment->ID, $where_lowest_id, $where_highest_id );
129
 
 
 
 
 
 
130
  if ( $where_lowest_id <= 0 ) {
131
  // Batch completed
132
  return true;
134
 
135
  $where_highest_id = $where_lowest_id;
136
  $where_lowest_id = max( $where_lowest_id - $limit, 0 );
137
+
138
+ $this->check_batch_limits();
139
  }
140
 
141
+ $this->last_post_id = $where_lowest_id;
142
 
143
  return false;
144
  }
145
 
146
+ /**
147
+ * Perform any actions necessary after the given item is completed.
148
+ *
149
+ * @param $item
150
+ */
151
+ protected function item_upgrade_completed( $item ) {
152
+ parent::item_upgrade_completed( $item );
153
+ $this->last_item = $item->ID;
154
+ }
155
+
156
  /**
157
  * Find and replace embedded URLs for an attachment.
158
  *
229
 
230
  // Remove URL protocols
231
  foreach ( $url_pairs as $key => $url_pair ) {
232
+ $url_pairs[ $key ]['old_url'] = AS3CF_Utils::remove_scheme( $url_pair['old_url'] );
233
+ $url_pairs[ $key ]['new_url'] = AS3CF_Utils::remove_scheme( $url_pair['new_url'] );
234
  }
235
 
236
  return apply_filters( 'as3cf_update_' . $this->upgrade_name . '_url_pairs', $url_pairs, $file_path, $old_url, $new_url, $meta );
330
 
331
  // Get unique URLs without size string and extension
332
  foreach ( $url_pairs as $url_pair ) {
333
+ $paths[] = AS3CF_Utils::remove_size_from_filename( $url_pair['old_url'], true );
334
  }
335
 
336
  $paths = array_unique( $paths );
395
  */
396
  protected function get_generic_message() {
397
  $link_text = __( 'See our documentation', 'amazon-s3-and-cloudfront' );
398
+ $url = $this->as3cf->dbrains_url( '/wp-offload-s3/doc/version-1-2-upgrade', array(
399
+ 'utm_campaign' => 'support+docs',
400
+ ) );
401
+ $link = AS3CF_Utils::dbrains_link( $url, $link_text );
402
 
403
  return sprintf( __( '%s for details on why we&#8217;re doing this, why it runs slowly, and how to make it run faster.', 'amazon-s3-and-cloudfront' ), $link );
404
  }
405
 
406
  /**
407
+ * Load the last blog ID from the session.
408
  *
409
+ * If the ID is found using the standard session key, use that.
410
+ * Otherwise if it is an older session, derive the ID from the blogs in the session.
411
+ *
412
+ * @return bool|int|mixed
413
  */
414
+ protected function load_last_blog_id() {
415
+ if ( $blog_id = parent::load_last_blog_id() ) {
416
+ return $blog_id;
417
+ }
418
+
419
+ $blog_ids = $this->load_processesed_blog_ids();
420
+
421
+ return end( $blog_ids );
422
+ }
423
 
424
+ /**
425
+ * Get all of the processed blog IDs from the session.
426
+ *
427
+ * @return array
428
+ */
429
+ protected function load_processesed_blog_ids() {
430
+ if ( $ids = parent::load_processesed_blog_ids() ) {
431
+ return $ids;
432
  }
433
 
434
+ if ( isset( $this->session['blogs'] ) && is_array( $this->session['blogs'] ) ) {
435
+ return array_keys( $this->session['blogs'] );
 
436
  }
437
 
438
+ return array();
439
  }
440
 
441
  /**
442
+ * Populate the last post ID.
443
  *
444
+ * The last post ID is set from the session if set,
445
+ * otherwise it defaults to the highest post ID on the site.
446
+ *
447
+ * @return int Post ID.
448
  */
449
+ protected function load_last_post_id() {
450
+ if ( isset( $this->session['last_post_id'] ) ) {
451
+ return (int) $this->session['last_post_id'];
452
  }
453
 
454
+ return $this->get_highest_post_id();
455
  }
456
  }
classes/upgrades/{as3cf-meta-wp-error.php → upgrade-meta-wp-error.php} RENAMED
@@ -9,13 +9,13 @@
9
  * @since 0.9.5
10
  */
11
 
12
- // Exit if accessed directly
13
- if ( ! defined( 'ABSPATH' ) ) {
14
- exit;
15
- }
16
 
17
  /**
18
- * AS3CF_Upgrade_Meta_WP_Error Class
19
  *
20
  * This class handles updating the _wp_attachment_metadata
21
  * for attachments that have been removed from the local server
@@ -23,7 +23,7 @@ if ( ! defined( 'ABSPATH' ) ) {
23
  *
24
  * @since 0.9.5
25
  */
26
- class AS3CF_Upgrade_Meta_WP_Error extends AS3CF_Upgrade {
27
 
28
  /**
29
  * @var int
@@ -95,22 +95,12 @@ class AS3CF_Upgrade_Meta_WP_Error extends AS3CF_Upgrade {
95
  }
96
 
97
  /**
98
- * Get a count of all attachments without region in their S3 metadata
99
- * for the whole site
100
  *
101
  * @return int
102
  */
103
  protected function count_items_to_process() {
104
- // get the table prefixes for all the blogs
105
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
106
- $all_count = 0;
107
-
108
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
109
- $count = $this->get_attachments_with_error_metadata( $table_prefix, true );
110
- $all_count += $count;
111
- }
112
-
113
- return $all_count;
114
  }
115
 
116
  /**
@@ -155,10 +145,8 @@ class AS3CF_Upgrade_Meta_WP_Error extends AS3CF_Upgrade {
155
 
156
  $sql = "SELECT pm1.`post_id` as `ID`, pm1.`meta_value` AS 's3object'" . $sql;
157
 
158
- if ( ! is_null( $limit ) ) {
159
- $sql .= ' LIMIT %d';
160
-
161
- $sql = $wpdb->prepare( $sql, $limit );
162
  }
163
 
164
  return $wpdb->get_results( $sql, OBJECT );
9
  * @since 0.9.5
10
  */
11
 
12
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
13
+
14
+ use AS3CF_Error;
15
+ use Exception;
16
 
17
  /**
18
+ * Upgrade_Meta_WP_Error Class
19
  *
20
  * This class handles updating the _wp_attachment_metadata
21
  * for attachments that have been removed from the local server
23
  *
24
  * @since 0.9.5
25
  */
26
+ class Upgrade_Meta_WP_Error extends Upgrade {
27
 
28
  /**
29
  * @var int
95
  }
96
 
97
  /**
98
+ * Get a count of all attachments without region in their S3 metadata.
 
99
  *
100
  * @return int
101
  */
102
  protected function count_items_to_process() {
103
+ return (int) $this->get_attachments_with_error_metadata( $this->blog_prefix, true );
 
 
 
 
 
 
 
 
 
104
  }
105
 
106
  /**
145
 
146
  $sql = "SELECT pm1.`post_id` as `ID`, pm1.`meta_value` AS 's3object'" . $sql;
147
 
148
+ if ( $limit && $limit > 0 ) {
149
+ $sql .= sprintf( ' LIMIT %d', (int) $limit );
 
 
150
  }
151
 
152
  return $wpdb->get_results( $sql, OBJECT );
classes/upgrades/{as3cf-region-meta.php → upgrade-region-meta.php} RENAMED
@@ -9,19 +9,18 @@
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
  * @var int
@@ -81,16 +80,7 @@ class AS3CF_Upgrade_Region_Meta extends AS3CF_Upgrade {
81
  * @return int
82
  */
83
  protected function count_items_to_process() {
84
- // get the table prefixes for all the blogs
85
- $table_prefixes = $this->as3cf->get_all_blog_table_prefixes();
86
- $all_count = 0;
87
-
88
- foreach ( $table_prefixes as $blog_id => $table_prefix ) {
89
- $count = $this->count_attachments_without_region( $table_prefix );
90
- $all_count += $count;
91
- }
92
-
93
- return $all_count;
94
  }
95
 
96
  /**
@@ -110,6 +100,7 @@ class AS3CF_Upgrade_Region_Meta extends AS3CF_Upgrade {
110
 
111
  /**
112
  * Get a count of attachments that don't have region in their S3 meta data for a blog
 
113
  * @param $prefix
114
  *
115
  * @return int
@@ -144,10 +135,8 @@ class AS3CF_Upgrade_Region_Meta extends AS3CF_Upgrade {
144
 
145
  $sql = "SELECT `post_id` as `ID`, `meta_value` AS 's3object'" . $sql;
146
 
147
- if ( ! is_null( $limit ) ) {
148
- $sql .= ' LIMIT %d';
149
-
150
- $sql = $wpdb->prepare( $sql, $limit );
151
  }
152
 
153
  return $wpdb->get_results( $sql, OBJECT );
9
  * @since 0.6.2
10
  */
11
 
12
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
13
+
14
+ use AS3CF_Error;
 
15
 
16
  /**
17
+ * Upgrade_Region_Meta Class
18
  *
19
  * This class handles updating the region of the attachment's bucket in the meta data
20
  *
21
  * @since 0.6.2
22
  */
23
+ class Upgrade_Region_Meta extends Upgrade {
24
 
25
  /**
26
  * @var int
80
  * @return int
81
  */
82
  protected function count_items_to_process() {
83
+ return $this->count_attachments_without_region( $this->blog_prefix );
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
  /**
100
 
101
  /**
102
  * Get a count of attachments that don't have region in their S3 meta data for a blog
103
+ *
104
  * @param $prefix
105
  *
106
  * @return int
135
 
136
  $sql = "SELECT `post_id` as `ID`, `meta_value` AS 's3object'" . $sql;
137
 
138
+ if ( $limit && $limit > 0 ) {
139
+ $sql .= sprintf( ' LIMIT %d', (int) $limit );
 
 
140
  }
141
 
142
  return $wpdb->get_results( $sql, OBJECT );
classes/upgrades/upgrade.php ADDED
@@ -0,0 +1,939 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Upgrade
4
+ *
5
+ * @package amazon-s3-and-cloudfront
6
+ * @subpackage Classes/Upgrade
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
+ namespace DeliciousBrains\WP_Offload_S3\Upgrades;
13
+
14
+ use Amazon_S3_And_CloudFront;
15
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions\No_More_Blogs_Exception;
16
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions\Batch_Limits_Exceeded_Exception;
17
+ use DeliciousBrains\WP_Offload_S3\Upgrades\Exceptions\Too_Many_Errors_Exception;
18
+ use WP_Error;
19
+
20
+ /**
21
+ * Upgrade Class
22
+ *
23
+ * This class handles updates to attachments and attachment meta data
24
+ *
25
+ * @since 0.6.2
26
+ */
27
+ abstract class Upgrade {
28
+
29
+ /**
30
+ * @var Amazon_S3_And_CloudFront
31
+ */
32
+ protected $as3cf;
33
+
34
+ /**
35
+ * @var int
36
+ */
37
+ protected $upgrade_id = 0;
38
+
39
+ /**
40
+ * @var string
41
+ */
42
+ protected $upgrade_name = 'base';
43
+
44
+ /**
45
+ * @var string 'metadata', 'attachment'
46
+ */
47
+ protected $upgrade_type = 'attachment';
48
+
49
+ /**
50
+ * @var string
51
+ */
52
+ protected $running_update_text;
53
+
54
+ /**
55
+ * @var string
56
+ */
57
+ protected $settings_key = 'post_meta_version';
58
+
59
+ /**
60
+ * @var string
61
+ */
62
+ protected $cron_hook;
63
+
64
+ /**
65
+ * @var string
66
+ */
67
+ protected $cron_schedule_key;
68
+
69
+ /**
70
+ * @var mixed|void
71
+ */
72
+ protected $cron_interval_in_minutes;
73
+
74
+ /**
75
+ * @var mixed|void
76
+ */
77
+ protected $error_threshold;
78
+
79
+ /**
80
+ * @var int
81
+ */
82
+ protected $error_count;
83
+
84
+ /**
85
+ * @var string
86
+ */
87
+ protected $lock_key = 'as3cf_upgrade_lock';
88
+
89
+ /**
90
+ * @var int Time limit in seconds.
91
+ */
92
+ protected $time_limit = 20;
93
+
94
+ /**
95
+ * @var int Batch size limit for this request session.
96
+ */
97
+ protected $size_limit = 500;
98
+
99
+ /**
100
+ * @var int Finish time
101
+ */
102
+ protected $finish;
103
+
104
+ /**
105
+ * @var int Maximum number of items to be processed in a single request.
106
+ */
107
+ protected $max_items_processable;
108
+
109
+ /**
110
+ * @var int Number of items processed.
111
+ */
112
+ protected $items_processed;
113
+
114
+ /**
115
+ * @var mixed Last attachment processed.
116
+ */
117
+ protected $last_item;
118
+
119
+ /**
120
+ * @var int The current blog ID.
121
+ */
122
+ protected $blog_id;
123
+
124
+ /**
125
+ * @var string The wpdb prefix for the current blog.
126
+ */
127
+ protected $blog_prefix;
128
+
129
+ /**
130
+ * @var int The last completed blog ID.
131
+ */
132
+ protected $last_blog_id;
133
+
134
+ /**
135
+ * @var array Blog IDs which are already processed.
136
+ */
137
+ protected $processed_blogs_ids;
138
+
139
+ /**
140
+ * @var array Session data
141
+ */
142
+ protected $session;
143
+
144
+ const STATUS_RUNNING = 1;
145
+ const STATUS_ERROR = 2;
146
+ const STATUS_PAUSED = 3;
147
+
148
+ /**
149
+ * Start it up
150
+ *
151
+ * @param Amazon_S3_And_CloudFront $as3cf - the instance of the as3cf class
152
+ */
153
+ public function __construct( $as3cf ) {
154
+ $this->as3cf = $as3cf;
155
+
156
+ $this->running_update_text = $this->get_running_update_text();
157
+ $this->cron_hook = 'as3cf_cron_update_' . $this->upgrade_name;
158
+ $this->cron_schedule_key = 'as3cf_update_' . $this->upgrade_name . '_interval';
159
+
160
+ $this->cron_interval_in_minutes = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_interval', 2 );
161
+ $this->error_threshold = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_error_threshold', 20 );
162
+ $this->max_items_processable = apply_filters( 'as3cf_update_' . $this->upgrade_name . '_batch_size', $this->size_limit );
163
+
164
+ add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
165
+ add_action( $this->cron_hook, array( $this, 'do_upgrade' ) );
166
+
167
+ add_action( 'as3cf_pre_settings_render', array( $this, 'maybe_display_notices' ) );
168
+ add_action( 'admin_init', array( $this, 'maybe_handle_action' ) );
169
+
170
+ // Do default checks if the upgrade can be started
171
+ if ( $this->maybe_init() ) {
172
+ $this->init();
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Can we start the upgrade using default checks
178
+ *
179
+ * @return bool
180
+ */
181
+ protected function maybe_init() {
182
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
183
+ return false;
184
+ }
185
+
186
+ if ( ! $this->screen_can_init() ) {
187
+ return false;
188
+ }
189
+
190
+ if ( ! $this->as3cf->is_plugin_setup() ) {
191
+ return false;
192
+ }
193
+
194
+ if ( $this->is_completed() ) {
195
+ return false;
196
+ }
197
+
198
+ if ( ! $this->has_previous_upgrade_completed() ) {
199
+ return false;
200
+ }
201
+
202
+ if ( $this->is_locked() ) {
203
+ return false;
204
+ }
205
+
206
+ // If the upgrade status is already set, then we've already initialized the upgrade
207
+ if ( $this->get_upgrade_status() ) {
208
+ if ( $this->is_running() ) {
209
+ // Make sure cron job is persisted in case it has dropped
210
+ $this->schedule();
211
+ }
212
+
213
+ return false;
214
+ }
215
+
216
+ return true;
217
+ }
218
+
219
+ /**
220
+ * Count items left to process for the current blog.
221
+ *
222
+ * @return int
223
+ */
224
+ protected function count_items_to_process() {
225
+ return count( $this->get_items_to_process( $this->blog_prefix, false, $this->last_item ) );
226
+ }
227
+
228
+ /**
229
+ * Get items to process.
230
+ *
231
+ * @param string $prefix
232
+ * @param int $limit
233
+ * @param bool|mixed $offset
234
+ *
235
+ * @return array
236
+ */
237
+ abstract protected function get_items_to_process( $prefix, $limit, $offset = false );
238
+
239
+ /**
240
+ * Upgrade attachment.
241
+ *
242
+ * @param mixed $attachment
243
+ *
244
+ * @return bool
245
+ */
246
+ abstract protected function upgrade_item( $attachment );
247
+
248
+ /**
249
+ * Get running update text.
250
+ *
251
+ * @return string
252
+ */
253
+ abstract protected function get_running_update_text();
254
+
255
+ /**
256
+ * Fire up the upgrade
257
+ */
258
+ protected function init() {
259
+ // Initialize the upgrade
260
+ $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
261
+
262
+ $this->schedule();
263
+ }
264
+
265
+ /**
266
+ * WP Cron callback to run the upgrade.
267
+ */
268
+ public function do_upgrade() {
269
+ $this->lock_upgrade();
270
+ $this->start_timer();
271
+
272
+ if ( $this->is_completed() || ! $this->is_running() ) {
273
+ $this->unschedule();
274
+
275
+ return;
276
+ }
277
+
278
+ $this->boot_session();
279
+ $this->run_upgrade();
280
+ }
281
+
282
+ /**
283
+ * Run or resume the main upgrade process.
284
+ */
285
+ protected function run_upgrade() {
286
+ try {
287
+ $blog_id = $this->get_initial_blog_id();
288
+
289
+ do {
290
+ $this->switch_to_blog( $blog_id );
291
+ $this->check_batch_limits();
292
+
293
+ if ( $this->upgrade_blog() ) {
294
+ $this->blog_upgrade_completed();
295
+ } else {
296
+
297
+ // If the blog didn't complete, break and try again next time before moving on.
298
+ break;
299
+ }
300
+ } while ( $blog_id = $this->next_blog_id() );
301
+
302
+ } catch ( No_More_Blogs_Exception $e ) {
303
+ /*
304
+ * The upgrade is complete when there are no more blogs left to finish.
305
+ */
306
+ $this->upgrade_finished();
307
+
308
+ return;
309
+ } catch ( Too_Many_Errors_Exception $e ) {
310
+ $this->upgrade_error();
311
+
312
+ return;
313
+ } catch ( Batch_Limits_Exceeded_Exception $e ) {
314
+
315
+ // Save the session and finish this round right away.
316
+ }
317
+
318
+ $this->close_session();
319
+ $this->save_session( $this->session );
320
+ }
321
+
322
+ /**
323
+ * Upgrade the current blog.
324
+ *
325
+ * @throws Too_Many_Errors_Exception
326
+ * @throws Batch_Limits_Exceeded_Exception
327
+ *
328
+ * @return bool true if all items for the blog were upgraded, otherwise false.
329
+ */
330
+ protected function upgrade_blog() {
331
+ $total = $this->count_items_to_process();
332
+ $items = $this->blog_batch_items();
333
+ $upgraded = 0;
334
+
335
+ foreach ( $items as $item ) {
336
+ if ( $this->upgrade_item( $item ) ) {
337
+ $this->item_upgrade_completed( $item );
338
+ $upgraded++;
339
+ }
340
+
341
+ // Items always count towards processing limits.
342
+ $this->items_processed++;
343
+
344
+ $this->check_batch_limits();
345
+ }
346
+
347
+ /*
348
+ * If the number upgraded is the same as the remaining total to process
349
+ * then all items have been upgraded for this blog.
350
+ */
351
+ if ( $upgraded === (int) $total ) {
352
+ return true;
353
+ }
354
+
355
+ return false;
356
+ }
357
+
358
+ /**
359
+ * Get the next sequential blog ID if there is one.
360
+ *
361
+ * @throws No_More_Blogs_Exception
362
+ *
363
+ * @return int
364
+ */
365
+ protected function next_blog_id() {
366
+ $blog_id = $this->blog_id ?: $this->last_blog_id;
367
+
368
+ do {
369
+ $blog_id--;
370
+
371
+ if ( $blog_id < 1 ) {
372
+ throw new No_More_Blogs_Exception;
373
+ }
374
+
375
+ } while ( ! $this->is_blog_processable( $blog_id ) );
376
+
377
+ return $blog_id;
378
+ }
379
+
380
+ /**
381
+ * Get the maximum number of processable items for the current blog,
382
+ * limited by the remaining number of items possible to process for this request.
383
+ *
384
+ * @return array
385
+ */
386
+ protected function blog_batch_items() {
387
+ $limit = $this->max_items_processable - $this->items_processed;
388
+
389
+ return $this->get_items_to_process( $this->blog_prefix, $limit, $this->last_item );
390
+ }
391
+
392
+ /**
393
+ * Adds notices about issues with upgrades allowing user to restart them
394
+ */
395
+ public function maybe_display_notices() {
396
+ $action_url = $this->as3cf->get_plugin_page_url( array(
397
+ 'action' => 'restart_update',
398
+ 'update' => $this->upgrade_name,
399
+ ), 'self' );
400
+ $msg_type = 'notice-info';
401
+
402
+ switch ( $this->get_upgrade_status() ) {
403
+ case self::STATUS_RUNNING:
404
+ $msg = $this->get_running_message();
405
+ $action_text = __( 'Pause Update', 'amazon-s3-and-cloudfront' );
406
+ $action_url = $this->as3cf->get_plugin_page_url( array(
407
+ 'action' => 'pause_update',
408
+ 'update' => $this->upgrade_name,
409
+ ), 'self' );
410
+ break;
411
+ case self::STATUS_PAUSED:
412
+ $msg = $this->get_paused_message();
413
+ $action_text = __( 'Restart Update', 'amazon-s3-and-cloudfront' );
414
+ break;
415
+ case self::STATUS_ERROR:
416
+ $msg = $this->get_error_message();
417
+ $action_text = __( 'Try Run It Again', 'amazon-s3-and-cloudfront' );
418
+ $msg_type = 'error';
419
+ break;
420
+ default:
421
+ return;
422
+ }
423
+
424
+ $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
425
+
426
+ $args = array(
427
+ 'message' => $msg,
428
+ 'type' => $msg_type,
429
+ );
430
+
431
+ $this->as3cf->render_view( 'notice', $args );
432
+ }
433
+
434
+ /**
435
+ * Get running message.
436
+ *
437
+ * @return string
438
+ */
439
+ protected function get_running_message() {
440
+ return sprintf( __( '<strong>Running %1$s Update%2$s</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 %3$s This will be done quietly in the background, processing a small batch of Media Library items every %4$d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'amazon-s3-and-cloudfront' ),
441
+ ucwords( $this->upgrade_type ),
442
+ $this->get_progress_text(),
443
+ $this->running_update_text,
444
+ $this->cron_interval_in_minutes
445
+ );
446
+ }
447
+
448
+ /**
449
+ * Get paused message.
450
+ *
451
+ * @return string
452
+ */
453
+ protected function get_paused_message() {
454
+ return sprintf( __( '<strong>%1$s Update Paused%2$s</strong> &mdash; Updating Media Library %3$s has been paused.', 'amazon-s3-and-cloudfront' ),
455
+ ucwords( $this->upgrade_type ),
456
+ $this->get_progress_text(),
457
+ $this->upgrade_type
458
+ );
459
+ }
460
+
461
+ /**
462
+ * Get error message.
463
+ *
464
+ * @return string
465
+ */
466
+ protected function get_error_message() {
467
+ return sprintf( __( '<strong>Error Updating %1$s</strong> &mdash; We ran into some errors attempting to update the %2$s for all your Media Library items that have been uploaded to S3. Please check your error log for details. (#%3$d)', 'amazon-s3-and-cloudfront' ),
468
+ ucwords( $this->upgrade_type ),
469
+ $this->upgrade_type,
470
+ $this->upgrade_id
471
+ );
472
+ }
473
+
474
+ /**
475
+ * Get progress text.
476
+ *
477
+ * @return string
478
+ */
479
+ protected function get_progress_text() {
480
+ $progress = $this->calculate_progress();
481
+
482
+ if ( false === $progress ) {
483
+ // Progress can not be calculated, return
484
+ return '';
485
+ }
486
+
487
+ if ( $progress > 100 ) {
488
+ $progress = 100;
489
+ }
490
+
491
+ return sprintf( __( ' (%s%% Complete)', 'amazon-s3-and-cloudfront' ), $progress );
492
+ }
493
+
494
+ /**
495
+ * Calculate progress.
496
+ *
497
+ * @return bool|float
498
+ */
499
+ protected function calculate_progress() {
500
+ $this->boot_session();
501
+
502
+ if ( is_multisite() ) {
503
+ $all_blog_ids = $this->as3cf->get_blog_ids();
504
+ $decimal = count( $this->processed_blogs_ids ) / count( $all_blog_ids );
505
+ } else {
506
+ // Set up any per-site state
507
+ $this->switch_to_blog( get_current_blog_id() );
508
+ $total_items = $this->as3cf->count_attachments( $this->blog_prefix );
509
+
510
+ // If there are no attachments, disable progress calculation
511
+ // and protect against division by zero.
512
+ if ( ! $total_items ) {
513
+ return false;
514
+ }
515
+
516
+ $remaining = $this->count_items_to_process();
517
+ $decimal = ( $total_items - $remaining ) / $total_items;
518
+ }
519
+
520
+ return round( $decimal * 100, 2 );
521
+ }
522
+
523
+ /**
524
+ * Handler for the running upgrade actions
525
+ */
526
+ public function maybe_handle_action() {
527
+ if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== $this->as3cf->get_plugin_slug() ) { // input var okay
528
+ return;
529
+ }
530
+
531
+ if ( ! isset( $_GET['action'] ) ) {
532
+ return;
533
+ }
534
+
535
+ if ( ! isset( $_GET['update'] ) || sanitize_key( $_GET['update'] ) !== $this->upgrade_name ) { // input var okay
536
+ return;
537
+ }
538
+
539
+ $method_name = 'action_' . sanitize_key( $_GET['action'] ); // input var okay
540
+
541
+ if ( method_exists( $this, $method_name ) ) {
542
+ call_user_func( array( $this, $method_name ) );
543
+ }
544
+ }
545
+
546
+ /**
547
+ * Exit upgrade with an error
548
+ */
549
+ protected function upgrade_error() {
550
+ $this->close_session();
551
+ $this->session['status'] = self::STATUS_ERROR;
552
+ $this->save_session( $this->session );
553
+ $this->unschedule();
554
+ }
555
+
556
+ /**
557
+ * Complete the upgrade
558
+ */
559
+ protected function upgrade_finished() {
560
+ $this->clear_session();
561
+ $this->update_saved_upgrade_id();
562
+ $this->unlock_upgrade();
563
+ $this->unschedule();
564
+ }
565
+
566
+ /**
567
+ * Restart upgrade
568
+ */
569
+ protected function action_restart_update() {
570
+ $this->schedule();
571
+ $this->change_status_request( self::STATUS_RUNNING );
572
+ }
573
+
574
+ /**
575
+ * Pause upgrade
576
+ */
577
+ protected function action_pause_update() {
578
+ $this->unschedule();
579
+
580
+ if ( $this->is_running() ) {
581
+ $this->change_status_request( self::STATUS_PAUSED );
582
+ }
583
+ }
584
+
585
+ /**
586
+ * Helper for the above action requests
587
+ *
588
+ * @param int $status
589
+ */
590
+ protected function change_status_request( $status ) {
591
+ $session = $this->get_session();
592
+ $session['status'] = $status;
593
+ $this->save_session( $session );
594
+
595
+ $url = $this->as3cf->get_plugin_page_url( array(), 'self' );
596
+ wp_redirect( $url );
597
+ exit;
598
+ }
599
+
600
+ /**
601
+ * Schedule the cron
602
+ */
603
+ protected function schedule() {
604
+ $this->as3cf->schedule_event( $this->cron_hook, $this->cron_schedule_key );
605
+ }
606
+
607
+ /**
608
+ * Remove the cron schedule
609
+ */
610
+ protected function unschedule() {
611
+ $this->as3cf->clear_scheduled_event( $this->cron_hook );
612
+ }
613
+
614
+ /**
615
+ * Add custom cron interval schedules
616
+ *
617
+ * @param array $schedules
618
+ *
619
+ * @return array
620
+ */
621
+ public function cron_schedules( $schedules ) {
622
+ // Add the upgrade interval to the existing schedules.
623
+ $schedules[ $this->cron_schedule_key ] = array(
624
+ 'interval' => $this->cron_interval_in_minutes * 60,
625
+ 'display' => sprintf( __( 'Every %d Minutes', 'amazon-s3-and-cloudfront' ), $this->cron_interval_in_minutes ),
626
+ );
627
+
628
+ return $schedules;
629
+ }
630
+
631
+ /**
632
+ * Get the current status of the upgrade
633
+ * See STATUS_* constants in the class declaration above.
634
+ */
635
+ protected function get_upgrade_status() {
636
+ $session = $this->get_session();
637
+
638
+ if ( ! isset( $session['status'] ) ) {
639
+ return '';
640
+ }
641
+
642
+ return $session['status'];
643
+ }
644
+
645
+ /**
646
+ * Retrieve session data from plugin settings
647
+ *
648
+ * @return array
649
+ */
650
+ protected function get_session() {
651
+ return get_site_option( 'update_' . $this->upgrade_name . '_session', array() );
652
+ }
653
+
654
+ /**
655
+ * Store data to be used between requests in plugin settings
656
+ *
657
+ * @param array $session session data to store
658
+ */
659
+ protected function save_session( $session ) {
660
+ update_site_option( 'update_' . $this->upgrade_name . '_session', $session );
661
+ }
662
+
663
+ /**
664
+ * Remove the session data to be used between requests
665
+ *
666
+ */
667
+ protected function clear_session() {
668
+ delete_site_option( 'update_' . $this->upgrade_name . '_session' );
669
+ }
670
+
671
+ /**
672
+ * Get the saved upgrade ID
673
+ *
674
+ * @return int|mixed|string|WP_Error
675
+ */
676
+ protected function get_saved_upgrade_id() {
677
+ return $this->as3cf->get_setting( $this->settings_key, 0 );
678
+ }
679
+
680
+ /**
681
+ * Update the saved upgrade ID
682
+ */
683
+ protected function update_saved_upgrade_id() {
684
+ $this->as3cf->set_setting( $this->settings_key, $this->upgrade_id );
685
+ $this->as3cf->save_settings();
686
+ }
687
+
688
+ /**
689
+ * Has previous upgrade completed
690
+ *
691
+ * @return bool
692
+ */
693
+ protected function has_previous_upgrade_completed() {
694
+ // Has the previous upgrade completed yet?
695
+ $previous_id = $this->upgrade_id - 1;
696
+ if ( 0 !== $previous_id && (int) $this->get_saved_upgrade_id() < $previous_id ) {
697
+ // Previous still running, abort
698
+ return false;
699
+ }
700
+
701
+ return true;
702
+ }
703
+
704
+ /**
705
+ * Lock upgrade.
706
+ */
707
+ protected function lock_upgrade() {
708
+ set_site_transient( $this->lock_key, $this->upgrade_id, MINUTE_IN_SECONDS * 3 );
709
+ }
710
+
711
+ /**
712
+ * Unlock the upgrade.
713
+ *
714
+ * Voids the lock after 1 second rather than deleting to avoid a race condition.
715
+ */
716
+ protected function unlock_upgrade() {
717
+ set_site_transient( $this->lock_key, $this->upgrade_id, 1 );
718
+ }
719
+
720
+ /**
721
+ * Whether or not the upgrade lock has been set.
722
+ *
723
+ * @return bool
724
+ */
725
+ protected function is_locked() {
726
+ return false !== get_site_transient( $this->lock_key );
727
+ }
728
+
729
+ /**
730
+ * Whether this upgrade has been completed or not.
731
+ *
732
+ * @return bool
733
+ */
734
+ protected function is_completed() {
735
+ return $this->get_saved_upgrade_id() >= $this->upgrade_id;
736
+ }
737
+
738
+ /**
739
+ * Whether this upgrade is currently running or not.
740
+ *
741
+ * @return bool
742
+ */
743
+ protected function is_running() {
744
+ return self::STATUS_RUNNING === $this->get_upgrade_status();
745
+ }
746
+
747
+ /**
748
+ * Set the time when the upgrade must finish by.
749
+ */
750
+ protected function start_timer() {
751
+ $this->finish = time() + apply_filters( 'as3cf_update_' . $this->upgrade_name . '_time_limit', $this->time_limit );
752
+ }
753
+
754
+ /**
755
+ * Check to see if batch limits have been exceeded.
756
+ *
757
+ * @throws Batch_Limits_Exceeded_Exception
758
+ * @throws Too_Many_Errors_Exception
759
+ */
760
+ protected function check_batch_limits() {
761
+ if ( $this->error_count > $this->error_threshold ) {
762
+ throw new Too_Many_Errors_Exception;
763
+ }
764
+
765
+ if ( $this->items_processed > $this->max_items_processable ) {
766
+ throw new Batch_Limits_Exceeded_Exception( 'Item limit reached.' );
767
+ }
768
+
769
+ if ( time() > $this->finish ) {
770
+ throw new Batch_Limits_Exceeded_Exception( 'Time limit exceeded.' );
771
+ }
772
+
773
+ if ( $this->as3cf->memory_exceeded( 'as3cf_update_' . $this->upgrade_name . '_memory_exceeded' ) ) {
774
+ throw new Batch_Limits_Exceeded_Exception( 'Memory limit exceeded with ' . memory_get_usage( true ) / 1024 / 1024 . 'MB' );
775
+ }
776
+ }
777
+
778
+ /**
779
+ * Check if a blog exists for the given blog ID.
780
+ *
781
+ * @param $blog_id
782
+ *
783
+ * @return bool
784
+ */
785
+ protected function blog_exists( $blog_id ) {
786
+ static $all_ids;
787
+
788
+ if ( function_exists( 'get_site' ) ) {
789
+ return (bool) get_site( $blog_id );
790
+ }
791
+
792
+ if ( ! $all_ids ) {
793
+ $all_ids = $this->as3cf->get_blog_ids();
794
+ }
795
+
796
+ return in_array( $blog_id, $all_ids );
797
+ }
798
+
799
+ /**
800
+ * Get the largest blog ID on the network.
801
+ *
802
+ * @return null|string
803
+ */
804
+ protected function get_final_blog_id() {
805
+ global $wpdb;
806
+
807
+ if ( is_multisite() ) {
808
+ return $wpdb->get_var( "SELECT MAX(blog_id) FROM {$wpdb->prefix}blogs" );
809
+ }
810
+
811
+ return 1;
812
+ }
813
+
814
+ /**
815
+ * Get the initial blog ID to start iterating with.
816
+ *
817
+ * @return int
818
+ */
819
+ protected function get_initial_blog_id() {
820
+ if ( $this->last_blog_id ) {
821
+ return $this->next_blog_id();
822
+ }
823
+
824
+ return (int) $this->get_final_blog_id();
825
+ }
826
+
827
+ /**
828
+ * Whether the given blog ID is processable or not.
829
+ *
830
+ * @param int $blog_id
831
+ *
832
+ * @return bool
833
+ */
834
+ protected function is_blog_processable( $blog_id ) {
835
+ if ( in_array( $blog_id, $this->processed_blogs_ids ) ) {
836
+ return false;
837
+ }
838
+
839
+ return $this->blog_exists( $blog_id );
840
+ }
841
+
842
+ /**
843
+ * Populate the session properties from the saved state.
844
+ */
845
+ protected function boot_session() {
846
+ $this->session = $this->get_session();
847
+ $this->last_blog_id = $this->load_last_blog_id();
848
+ $this->processed_blogs_ids = $this->load_processesed_blog_ids();
849
+ $this->error_count = isset( $this->session['error_count'] ) ? $this->session['error_count'] : 0;
850
+ $this->last_item = $this->load_last_item();
851
+ }
852
+
853
+ /**
854
+ * Get all of the processed blog IDs from the session.
855
+ *
856
+ * @return array
857
+ */
858
+ protected function load_processesed_blog_ids() {
859
+ $session = $this->session ?: $this->get_session();
860
+
861
+ return isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
862
+ }
863
+
864
+ /**
865
+ * Mark the current blog upgrade as complete.
866
+ */
867
+ protected function blog_upgrade_completed() {
868
+ $this->last_blog_id = $this->blog_id;
869
+ $this->processed_blogs_ids[] = $this->blog_id;
870
+ $this->last_item = false;
871
+ }
872
+
873
+ /**
874
+ * Perform any actions necessary after the given item is completed.
875
+ *
876
+ * @param $item
877
+ */
878
+ protected function item_upgrade_completed( $item ) {
879
+ $this->last_item = $item;
880
+ }
881
+
882
+ /**
883
+ * Prepare the session to be persisted.
884
+ */
885
+ protected function close_session() {
886
+ $this->session['last_blog_id'] = $this->last_blog_id;
887
+ $this->session['offset'] = $this->last_item;
888
+ $this->session['error_count'] = $this->error_count;
889
+ $this->session['processed_blog_ids'] = $this->processed_blogs_ids;
890
+ }
891
+
892
+ /**
893
+ * Load the last completed blog ID from the session.
894
+ *
895
+ * @return bool|int
896
+ */
897
+ protected function load_last_blog_id() {
898
+ if ( ! empty( $this->session['last_blog_id'] ) ) {
899
+ return (int) $this->session['last_blog_id'];
900
+ }
901
+
902
+ return null;
903
+ }
904
+
905
+ /**
906
+ * Switch to the given blog, and update blog-specific state.
907
+ *
908
+ * @param $blog_id
909
+ *
910
+ * @throws Batch_Limits_Exceeded_Exception
911
+ */
912
+ protected function switch_to_blog( $blog_id ) {
913
+ $this->as3cf->switch_to_blog( $blog_id );
914
+ $this->blog_id = (int) $blog_id;
915
+ $this->blog_prefix = $GLOBALS['wpdb']->prefix;
916
+ }
917
+
918
+ /**
919
+ * Get the last processed item from the session.
920
+ *
921
+ * @return bool|mixed
922
+ */
923
+ protected function load_last_item() {
924
+ return isset( $this->session['offset'] ) ? $this->session['offset'] : false;
925
+ }
926
+
927
+ /**
928
+ * Whether or not the current screen can initialize the upgrade.
929
+ *
930
+ * @return bool
931
+ */
932
+ protected function screen_can_init() {
933
+ if ( is_multisite() ) {
934
+ return is_network_admin();
935
+ }
936
+
937
+ return is_admin();
938
+ }
939
+ }
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: 2017-03-13 13:16+0000\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,135 +17,137 @@ msgstr ""
17
  "Content-Type: text/plain; charset=UTF-8\n"
18
  "Content-Transfer-Encoding: 8bit\n"
19
 
20
- #: classes/amazon-s3-and-cloudfront.php:115
21
  msgid "Offload S3 Lite"
22
  msgstr ""
23
 
24
- #: classes/amazon-s3-and-cloudfront.php:116
25
  msgid "S3 and CloudFront"
26
  msgstr ""
27
 
28
- #: classes/amazon-s3-and-cloudfront.php:224
29
  #: view/bucket-setting.php:18
30
  msgid "defined in wp-config.php"
31
  msgstr ""
32
 
33
- #: classes/amazon-s3-and-cloudfront.php:886
34
  msgid "Upload aborted by filter 'as3cf_pre_upload_attachment'"
35
  msgstr ""
36
 
37
- #: classes/amazon-s3-and-cloudfront.php:897
 
38
  #, php-format
39
  msgid "File %s does not exist"
40
  msgstr ""
41
 
42
- #: classes/amazon-s3-and-cloudfront.php:908
43
  #, php-format
44
  msgid "Mime type %s is not allowed"
45
  msgstr ""
46
 
47
- #: classes/amazon-s3-and-cloudfront.php:993
 
48
  #, php-format
49
  msgid "Error uploading %s to S3: %s"
50
  msgstr ""
51
 
52
- #: classes/amazon-s3-and-cloudfront.php:2191
53
  msgid "Cheatin&#8217; eh?"
54
  msgstr ""
55
 
56
- #: classes/amazon-s3-and-cloudfront.php:2195
57
  msgid "You do not have sufficient permissions to access this page."
58
  msgstr ""
59
 
60
- #: classes/amazon-s3-and-cloudfront.php:2201
61
  msgid "No bucket name provided."
62
  msgstr ""
63
 
64
- #: classes/amazon-s3-and-cloudfront.php:2465
65
  msgid "Error Getting Bucket Region"
66
  msgstr ""
67
 
68
- #: classes/amazon-s3-and-cloudfront.php:2466
69
  #, php-format
70
  msgid "There was an error attempting to get the region of the bucket %s: %s"
71
  msgstr ""
72
 
73
- #: classes/amazon-s3-and-cloudfront.php:2585
74
  msgid ""
75
  "This is a test file to check if the user has write permission to S3. Delete "
76
  "me if found."
77
  msgstr ""
78
 
79
- #: classes/amazon-s3-and-cloudfront.php:2617
80
  #, php-format
81
  msgid ""
82
  "There was an error attempting to check the permissions of the bucket %s: %s"
83
  msgstr ""
84
 
85
- #: classes/amazon-s3-and-cloudfront.php:2674
86
  msgid "Error creating bucket"
87
  msgstr ""
88
 
89
- #: classes/amazon-s3-and-cloudfront.php:2675
90
  msgid "Bucket name too short."
91
  msgstr ""
92
 
93
- #: classes/amazon-s3-and-cloudfront.php:2676
94
  msgid "Bucket name too long."
95
  msgstr ""
96
 
97
- #: classes/amazon-s3-and-cloudfront.php:2677
98
  msgid ""
99
  "Invalid character. Bucket names can contain lowercase letters, numbers, "
100
  "periods and hyphens."
101
  msgstr ""
102
 
103
- #: classes/amazon-s3-and-cloudfront.php:2678
104
  msgid "Error saving bucket"
105
  msgstr ""
106
 
107
- #: classes/amazon-s3-and-cloudfront.php:2679
108
  msgid "Error fetching buckets"
109
  msgstr ""
110
 
111
- #: classes/amazon-s3-and-cloudfront.php:2680
112
  msgid "Error getting URL preview: "
113
  msgstr ""
114
 
115
- #: classes/amazon-s3-and-cloudfront.php:2681
116
  msgid "The changes you made will be lost if you navigate away from this page"
117
  msgstr ""
118
 
119
- #: classes/amazon-s3-and-cloudfront.php:2682
120
  msgid "Getting diagnostic info..."
121
  msgstr ""
122
 
123
- #: classes/amazon-s3-and-cloudfront.php:2683
124
  msgid "Error getting diagnostic info: "
125
  msgstr ""
126
 
127
- #: classes/amazon-s3-and-cloudfront.php:2751
128
  msgid "Cheatin' eh?"
129
  msgstr ""
130
 
131
- #: classes/amazon-s3-and-cloudfront.php:2858
132
  msgctxt "Show the media library tab"
133
  msgid "Media Library"
134
  msgstr ""
135
 
136
- #: classes/amazon-s3-and-cloudfront.php:2859
137
  msgctxt "Show the support tab"
138
  msgid "Support"
139
  msgstr ""
140
 
141
- #: classes/amazon-s3-and-cloudfront.php:3076
142
  #, php-format
143
  msgid ""
144
  "<strong>WP Offload S3</strong> &mdash; The file %s has been given %s "
145
  "permissions on Amazon S3."
146
  msgstr ""
147
 
148
- #: classes/amazon-s3-and-cloudfront.php:3095
149
  msgid ""
150
  "<strong>WP Offload S3 Requirement Missing</strong> &mdash; Looks like you "
151
  "don't have an image manipulation library installed on this server and "
@@ -153,11 +155,11 @@ msgid ""
153
  "Please setup GD or ImageMagick."
154
  msgstr ""
155
 
156
- #: classes/amazon-s3-and-cloudfront.php:3774
157
  msgid "Quick Start Guide"
158
  msgstr ""
159
 
160
- #: classes/amazon-s3-and-cloudfront.php:3776
161
  #, php-format
162
  msgid ""
163
  "Looks like we don't have write access to this bucket. It's likely that the "
@@ -166,7 +168,7 @@ msgid ""
166
  "correctly."
167
  msgstr ""
168
 
169
- #: classes/amazon-s3-and-cloudfront.php:3778
170
  #, php-format
171
  msgid ""
172
  "Looks like we don't have access to the buckets. It's likely that the user "
@@ -174,39 +176,39 @@ msgid ""
174
  "Please see our %s for instructions on setting up permissions correctly."
175
  msgstr ""
176
 
177
- #: classes/amazon-s3-and-cloudfront.php:3929
178
  msgid "WP Offload S3 Activation"
179
  msgstr ""
180
 
181
- #: classes/amazon-s3-and-cloudfront.php:3930
182
  msgid ""
183
  "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've "
184
  "automatically deactivated WP Offload S3 Lite."
185
  msgstr ""
186
 
187
- #: classes/amazon-s3-and-cloudfront.php:3932
188
  msgid "WP Offload S3 Lite Activation"
189
  msgstr ""
190
 
191
- #: classes/amazon-s3-and-cloudfront.php:3933
192
  msgid ""
193
  "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've "
194
  "automatically deactivated WP Offload S3."
195
  msgstr ""
196
 
197
- #: classes/amazon-s3-and-cloudfront.php:4007
198
  msgid "More info"
199
  msgstr ""
200
 
201
- #: classes/amazon-s3-and-cloudfront.php:4098
202
  msgid "this doc"
203
  msgstr ""
204
 
205
- #: classes/amazon-s3-and-cloudfront.php:4100
206
  msgid "WP Offload S3 Feature Removed"
207
  msgstr ""
208
 
209
- #: classes/amazon-s3-and-cloudfront.php:4101
210
  #, php-format
211
  msgid ""
212
  "You had the \"Always non-SSL\" option selected in your settings, but we've "
@@ -217,115 +219,131 @@ msgid ""
217
  "to the old behavior."
218
  msgstr ""
219
 
220
- #: classes/amazon-s3-and-cloudfront.php:4131
221
- #: classes/amazon-s3-and-cloudfront.php:4229
222
  msgid "Amazon S3"
223
  msgstr ""
224
 
225
- #: classes/amazon-s3-and-cloudfront.php:4230
226
  msgctxt "Amazon S3 bucket"
227
  msgid "Bucket"
228
  msgstr ""
229
 
230
- #: classes/amazon-s3-and-cloudfront.php:4231
231
  msgctxt "Path to file on Amazon S3"
232
  msgid "Path"
233
  msgstr ""
234
 
235
- #: classes/amazon-s3-and-cloudfront.php:4232
236
  msgctxt "Location of Amazon S3 bucket"
237
  msgid "Region"
238
  msgstr ""
239
 
240
- #: classes/amazon-s3-and-cloudfront.php:4233
241
  msgctxt "Access control list of the file on Amazon S3"
242
  msgid "Access"
243
  msgstr ""
244
 
245
- #: classes/amazon-s3-and-cloudfront.php:4234
246
  msgid "URL"
247
  msgstr ""
248
 
249
- #: classes/as3cf-notices.php:438
250
  msgid "Error dismissing notice."
251
  msgstr ""
252
 
253
- #: classes/as3cf-notices.php:453
254
  msgid "Invalid notice ID."
255
  msgstr ""
256
 
257
- #: classes/as3cf-plugin-compatibility.php:193
258
- msgid "WP Offload S3 Compatibility Addons"
 
259
  msgstr ""
260
 
261
- #: classes/as3cf-plugin-compatibility.php:195
262
- msgid "compatibility addons"
 
 
 
263
  msgstr ""
264
 
265
- #: classes/as3cf-plugin-compatibility.php:196
266
- #, php-format
267
- msgid ""
268
- "To get WP Offload S3 to work with certain 3rd party plugins, you might need "
269
- "to install and activate some of our %s. We've detected the following addons "
270
- "might need to be installed. Please click the links for more information "
271
- "about each addon to determine if you need it or not."
272
  msgstr ""
273
 
274
- #: classes/as3cf-plugin-compatibility.php:202
275
  #, php-format
276
  msgid ""
277
- "You will need to purchase a license to get access to these addons. If you're "
278
- "having trouble determining whether or not you need the addons, send an email "
279
- "to %s."
280
  msgstr ""
281
 
282
- #: classes/as3cf-plugin-compatibility.php:203
283
- msgid "View Licenses"
284
  msgstr ""
285
 
286
- #: classes/as3cf-plugin-compatibility.php:669
287
- #, php-format
288
- msgid "The local directory %s does not exist and could not be created."
 
 
289
  msgstr ""
290
 
291
- #: classes/as3cf-plugin-compatibility.php:670
292
- #: classes/as3cf-plugin-compatibility.php:682
293
- #: classes/upgrades/as3cf-meta-wp-error.php:81
 
 
294
  #, php-format
295
- msgid "There was an error attempting to download the file %s from S3: %s"
 
 
296
  msgstr ""
297
 
298
- #: classes/as3cf-upgrade-filter-post.php:544
299
  #, php-format
300
  msgid ""
301
  "<strong>Paused Upgrade</strong><br>The find &amp; replace to update URLs has "
302
  "been paused. %s"
303
  msgstr ""
304
 
305
- #: classes/as3cf-upgrade-filter-post.php:553
306
  msgid "See our documentation"
307
  msgstr ""
308
 
309
- #: classes/as3cf-upgrade-filter-post.php:556
310
  #, php-format
311
  msgid ""
312
  "%s for details on why we&#8217;re doing this, why it runs slowly, and how to "
313
  "make it run faster."
314
  msgstr ""
315
 
316
- #: classes/as3cf-upgrade.php:318
 
 
 
 
 
 
 
 
 
 
 
 
317
  msgid "Pause Update"
318
  msgstr ""
319
 
320
- #: classes/as3cf-upgrade.php:326
321
  msgid "Restart Update"
322
  msgstr ""
323
 
324
- #: classes/as3cf-upgrade.php:330
325
  msgid "Try Run It Again"
326
  msgstr ""
327
 
328
- #: classes/as3cf-upgrade.php:353
329
  #, php-format
330
  msgid ""
331
  "<strong>Running %1$s Update%2$s</strong> &mdash; We&#8217;re going through "
@@ -335,14 +353,14 @@ msgid ""
335
  "performance."
336
  msgstr ""
337
 
338
- #: classes/as3cf-upgrade.php:367
339
  #, php-format
340
  msgid ""
341
  "<strong>%1$s Update Paused%2$s</strong> &mdash; Updating Media Library %3$s "
342
  "has been paused."
343
  msgstr ""
344
 
345
- #: classes/as3cf-upgrade.php:380
346
  #, php-format
347
  msgid ""
348
  "<strong>Error Updating %1$s</strong> &mdash; We ran into some errors "
@@ -350,61 +368,16 @@ msgid ""
350
  "been uploaded to S3. Please check your error log for details. (#%3$d)"
351
  msgstr ""
352
 
353
- #: classes/as3cf-upgrade.php:404
354
  #, php-format
355
  msgid " (%s%% Complete)"
356
  msgstr ""
357
 
358
- #: classes/as3cf-upgrade.php:515
359
  #, php-format
360
  msgid "Every %d Minutes"
361
  msgstr ""
362
 
363
- #: classes/upgrades/as3cf-file-sizes.php:48
364
- msgid ""
365
- "and updating the metadata with the sizes of files that have been removed "
366
- "from the server. This will allow us to serve the correct size for media "
367
- "items and the total space used in Multisite subsites."
368
- msgstr ""
369
-
370
- #: classes/upgrades/as3cf-filter-edd.php:39
371
- msgid "and ensuring that only the local URL exists in EDD post meta."
372
- msgstr ""
373
-
374
- #: classes/upgrades/as3cf-filter-post-content.php:39
375
- msgid "and ensuring that only the local URL exists in post content."
376
- msgstr ""
377
-
378
- #: classes/upgrades/as3cf-filter-post-content.php:48
379
- #, php-format
380
- msgid ""
381
- "<strong>Running Content Upgrade%1$s</strong><br>A find &amp; replace is "
382
- "running in the background to update URLs in your post content. %2$s"
383
- msgstr ""
384
-
385
- #: classes/upgrades/as3cf-filter-post-excerpt.php:39
386
- msgid "and ensuring that only the local URL exists in post excerpts."
387
- msgstr ""
388
-
389
- #: classes/upgrades/as3cf-filter-post-excerpt.php:48
390
- #, php-format
391
- msgid ""
392
- "<strong>Running Excerpts Upgrade%1$s</strong><br>A find &amp; replace is "
393
- "running in the background to update URLs in your post excerpts. %2$s"
394
- msgstr ""
395
-
396
- #: classes/upgrades/as3cf-meta-wp-error.php:49
397
- msgid ""
398
- "and rebuilding the metadata for attachments that may have been corrupted."
399
- msgstr ""
400
-
401
- #: classes/upgrades/as3cf-region-meta.php:47
402
- msgid ""
403
- "and updating the metadata with the bucket region it is served from. This "
404
- "will allow us to serve your files from the proper S3 region subdomain <span "
405
- "style=\"white-space:nowrap;\">(e.g. s3-us-west-2.amazonaws.com)</span>."
406
- msgstr ""
407
-
408
  #: classes/wp-aws-compatibility-check.php:323
409
  msgid "deactivate"
410
  msgstr ""
@@ -582,6 +555,12 @@ msgstr ""
582
  msgid "Change"
583
  msgstr ""
584
 
 
 
 
 
 
 
585
  #: view/cloudfront-setting.php:7
586
  #: view/deprecated-domain-setting.php:49
587
  msgid "Invalid character. Letters, numbers, periods and hyphens are allowed."
@@ -737,62 +716,62 @@ msgstr ""
737
  msgid "Save Changes"
738
  msgstr ""
739
 
740
- #: view/sidebar.php:10
741
  msgid "Upload existing Media Library to S3"
742
  msgstr ""
743
 
744
- #: view/sidebar.php:11
745
  msgid "Manage S3 files in WordPress"
746
  msgstr ""
747
 
748
- #: view/sidebar.php:12
749
  msgid "Assets addon - Serve your CSS & JS from S3/CloudFront"
750
  msgstr ""
751
 
752
- #: view/sidebar.php:13
753
  msgid "WooCommerce addon"
754
  msgstr ""
755
 
756
- #: view/sidebar.php:14
757
  msgid "Easy Digital Downloads addon"
758
  msgstr ""
759
 
760
- #: view/sidebar.php:15
761
  msgid "PriorityExpert™ email support"
762
  msgstr ""
763
 
764
- #: view/sidebar.php:18
765
  msgid "Visit deliciousbrains.com &rarr;"
766
  msgstr ""
767
 
768
- #: view/sidebar.php:25
769
  msgid "Get 20% Off!"
770
  msgstr ""
771
 
772
- #: view/sidebar.php:28
773
  #, php-format
774
  msgid ""
775
  "Submit your name and email and we’ll send you a coupon for 20% off your "
776
  "upgrade."
777
  msgstr ""
778
 
779
- #: view/sidebar.php:32
780
  msgid "Your Email"
781
  msgstr ""
782
 
783
- #: view/sidebar.php:36
784
  msgid "First Name"
785
  msgstr ""
786
 
787
- #: view/sidebar.php:40
788
  msgid "Last Name"
789
  msgstr ""
790
 
791
- #: view/sidebar.php:47
792
  msgid "Send me the coupon"
793
  msgstr ""
794
 
795
- #: view/sidebar.php:51
796
  msgid ""
797
  "We promise we will not use your email for anything else and you can "
798
  "unsubscribe with 1-click anytime."
@@ -810,14 +789,14 @@ msgid ""
810
  "to a few weeks and will likely be from a non-developer."
811
  msgstr ""
812
 
813
- #: view/wordpress-org-support.php:6
814
  #, php-format
815
  msgid ""
816
  "If you want a <strong>timely response via email from a developer</strong> "
817
  "who works on this plugin, <a href=\"%s\">upgrade</a> and send us an email."
818
  msgstr ""
819
 
820
- #: view/wordpress-org-support.php:8
821
  #, php-format
822
  msgid ""
823
  "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: 2017-06-19 15:09+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:122
21
  msgid "Offload S3 Lite"
22
  msgstr ""
23
 
24
+ #: classes/amazon-s3-and-cloudfront.php:123
25
  msgid "S3 and CloudFront"
26
  msgstr ""
27
 
28
+ #: classes/amazon-s3-and-cloudfront.php:232
29
  #: view/bucket-setting.php:18
30
  msgid "defined in wp-config.php"
31
  msgstr ""
32
 
33
+ #: classes/amazon-s3-and-cloudfront.php:900
34
  msgid "Upload aborted by filter 'as3cf_pre_upload_attachment'"
35
  msgstr ""
36
 
37
+ #: classes/amazon-s3-and-cloudfront.php:911
38
+ #: classes/amazon-s3-and-cloudfront.php:1071
39
  #, php-format
40
  msgid "File %s does not exist"
41
  msgstr ""
42
 
43
+ #: classes/amazon-s3-and-cloudfront.php:922
44
  #, php-format
45
  msgid "Mime type %s is not allowed"
46
  msgstr ""
47
 
48
+ #: classes/amazon-s3-and-cloudfront.php:1007
49
+ #: classes/amazon-s3-and-cloudfront.php:1079
50
  #, php-format
51
  msgid "Error uploading %s to S3: %s"
52
  msgstr ""
53
 
54
+ #: classes/amazon-s3-and-cloudfront.php:2223
55
  msgid "Cheatin&#8217; eh?"
56
  msgstr ""
57
 
58
+ #: classes/amazon-s3-and-cloudfront.php:2227
59
  msgid "You do not have sufficient permissions to access this page."
60
  msgstr ""
61
 
62
+ #: classes/amazon-s3-and-cloudfront.php:2233
63
  msgid "No bucket name provided."
64
  msgstr ""
65
 
66
+ #: classes/amazon-s3-and-cloudfront.php:2497
67
  msgid "Error Getting Bucket Region"
68
  msgstr ""
69
 
70
+ #: classes/amazon-s3-and-cloudfront.php:2498
71
  #, php-format
72
  msgid "There was an error attempting to get the region of the bucket %s: %s"
73
  msgstr ""
74
 
75
+ #: classes/amazon-s3-and-cloudfront.php:2626
76
  msgid ""
77
  "This is a test file to check if the user has write permission to S3. Delete "
78
  "me if found."
79
  msgstr ""
80
 
81
+ #: classes/amazon-s3-and-cloudfront.php:2645
82
  #, php-format
83
  msgid ""
84
  "There was an error attempting to check the permissions of the bucket %s: %s"
85
  msgstr ""
86
 
87
+ #: classes/amazon-s3-and-cloudfront.php:2699
88
  msgid "Error creating bucket"
89
  msgstr ""
90
 
91
+ #: classes/amazon-s3-and-cloudfront.php:2700
92
  msgid "Bucket name too short."
93
  msgstr ""
94
 
95
+ #: classes/amazon-s3-and-cloudfront.php:2701
96
  msgid "Bucket name too long."
97
  msgstr ""
98
 
99
+ #: classes/amazon-s3-and-cloudfront.php:2702
100
  msgid ""
101
  "Invalid character. Bucket names can contain lowercase letters, numbers, "
102
  "periods and hyphens."
103
  msgstr ""
104
 
105
+ #: classes/amazon-s3-and-cloudfront.php:2703
106
  msgid "Error saving bucket"
107
  msgstr ""
108
 
109
+ #: classes/amazon-s3-and-cloudfront.php:2704
110
  msgid "Error fetching buckets"
111
  msgstr ""
112
 
113
+ #: classes/amazon-s3-and-cloudfront.php:2705
114
  msgid "Error getting URL preview: "
115
  msgstr ""
116
 
117
+ #: classes/amazon-s3-and-cloudfront.php:2706
118
  msgid "The changes you made will be lost if you navigate away from this page"
119
  msgstr ""
120
 
121
+ #: classes/amazon-s3-and-cloudfront.php:2707
122
  msgid "Getting diagnostic info..."
123
  msgstr ""
124
 
125
+ #: classes/amazon-s3-and-cloudfront.php:2708
126
  msgid "Error getting diagnostic info: "
127
  msgstr ""
128
 
129
+ #: classes/amazon-s3-and-cloudfront.php:2776
130
  msgid "Cheatin' eh?"
131
  msgstr ""
132
 
133
+ #: classes/amazon-s3-and-cloudfront.php:2883
134
  msgctxt "Show the media library tab"
135
  msgid "Media Library"
136
  msgstr ""
137
 
138
+ #: classes/amazon-s3-and-cloudfront.php:2884
139
  msgctxt "Show the support tab"
140
  msgid "Support"
141
  msgstr ""
142
 
143
+ #: classes/amazon-s3-and-cloudfront.php:3101
144
  #, php-format
145
  msgid ""
146
  "<strong>WP Offload S3</strong> &mdash; The file %s has been given %s "
147
  "permissions on Amazon S3."
148
  msgstr ""
149
 
150
+ #: classes/amazon-s3-and-cloudfront.php:3120
151
  msgid ""
152
  "<strong>WP Offload S3 Requirement Missing</strong> &mdash; Looks like you "
153
  "don't have an image manipulation library installed on this server and "
155
  "Please setup GD or ImageMagick."
156
  msgstr ""
157
 
158
+ #: classes/amazon-s3-and-cloudfront.php:3729
159
  msgid "Quick Start Guide"
160
  msgstr ""
161
 
162
+ #: classes/amazon-s3-and-cloudfront.php:3731
163
  #, php-format
164
  msgid ""
165
  "Looks like we don't have write access to this bucket. It's likely that the "
168
  "correctly."
169
  msgstr ""
170
 
171
+ #: classes/amazon-s3-and-cloudfront.php:3733
172
  #, php-format
173
  msgid ""
174
  "Looks like we don't have access to the buckets. It's likely that the user "
176
  "Please see our %s for instructions on setting up permissions correctly."
177
  msgstr ""
178
 
179
+ #: classes/amazon-s3-and-cloudfront.php:3883
180
  msgid "WP Offload S3 Activation"
181
  msgstr ""
182
 
183
+ #: classes/amazon-s3-and-cloudfront.php:3884
184
  msgid ""
185
  "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've "
186
  "automatically deactivated WP Offload S3 Lite."
187
  msgstr ""
188
 
189
+ #: classes/amazon-s3-and-cloudfront.php:3886
190
  msgid "WP Offload S3 Lite Activation"
191
  msgstr ""
192
 
193
+ #: classes/amazon-s3-and-cloudfront.php:3887
194
  msgid ""
195
  "WP Offload S3 Lite and WP Offload S3 cannot both be active. We've "
196
  "automatically deactivated WP Offload S3."
197
  msgstr ""
198
 
199
+ #: classes/amazon-s3-and-cloudfront.php:3939
200
  msgid "More info"
201
  msgstr ""
202
 
203
+ #: classes/amazon-s3-and-cloudfront.php:4034
204
  msgid "this doc"
205
  msgstr ""
206
 
207
+ #: classes/amazon-s3-and-cloudfront.php:4036
208
  msgid "WP Offload S3 Feature Removed"
209
  msgstr ""
210
 
211
+ #: classes/amazon-s3-and-cloudfront.php:4037
212
  #, php-format
213
  msgid ""
214
  "You had the \"Always non-SSL\" option selected in your settings, but we've "
219
  "to the old behavior."
220
  msgstr ""
221
 
222
+ #: classes/amazon-s3-and-cloudfront.php:4067
223
+ #: classes/amazon-s3-and-cloudfront.php:4165
224
  msgid "Amazon S3"
225
  msgstr ""
226
 
227
+ #: classes/amazon-s3-and-cloudfront.php:4166
228
  msgctxt "Amazon S3 bucket"
229
  msgid "Bucket"
230
  msgstr ""
231
 
232
+ #: classes/amazon-s3-and-cloudfront.php:4167
233
  msgctxt "Path to file on Amazon S3"
234
  msgid "Path"
235
  msgstr ""
236
 
237
+ #: classes/amazon-s3-and-cloudfront.php:4168
238
  msgctxt "Location of Amazon S3 bucket"
239
  msgid "Region"
240
  msgstr ""
241
 
242
+ #: classes/amazon-s3-and-cloudfront.php:4169
243
  msgctxt "Access control list of the file on Amazon S3"
244
  msgid "Access"
245
  msgstr ""
246
 
247
+ #: classes/amazon-s3-and-cloudfront.php:4170
248
  msgid "URL"
249
  msgstr ""
250
 
251
+ #: classes/as3cf-notices.php:431
252
  msgid "Error dismissing notice."
253
  msgstr ""
254
 
255
+ #: classes/as3cf-notices.php:446
256
  msgid "Invalid notice ID."
257
  msgstr ""
258
 
259
+ #: classes/as3cf-plugin-compatibility.php:512
260
+ #, php-format
261
+ msgid "The local directory %s does not exist and could not be created."
262
  msgstr ""
263
 
264
+ #: classes/as3cf-plugin-compatibility.php:513
265
+ #: classes/as3cf-plugin-compatibility.php:525
266
+ #: classes/upgrades/upgrade-meta-wp-error.php:81
267
+ #, php-format
268
+ msgid "There was an error attempting to download the file %s from S3: %s"
269
  msgstr ""
270
 
271
+ #: classes/upgrades/upgrade-content-replace-urls.php:36
272
+ msgid "and ensuring that only the local URL exists in post content."
 
 
 
 
 
273
  msgstr ""
274
 
275
+ #: classes/upgrades/upgrade-content-replace-urls.php:45
276
  #, php-format
277
  msgid ""
278
+ "<strong>Running Content Upgrade%1$s</strong><br>A find &amp; replace is "
279
+ "running in the background to update URLs in your post content. %2$s"
 
280
  msgstr ""
281
 
282
+ #: classes/upgrades/upgrade-edd-replace-urls.php:36
283
+ msgid "and ensuring that only the local URL exists in EDD post meta."
284
  msgstr ""
285
 
286
+ #: classes/upgrades/upgrade-file-sizes.php:48
287
+ msgid ""
288
+ "and updating the metadata with the sizes of files that have been removed "
289
+ "from the server. This will allow us to serve the correct size for media "
290
+ "items and the total space used in Multisite subsites."
291
  msgstr ""
292
 
293
+ #: classes/upgrades/upgrade-filter-post-excerpt.php:36
294
+ msgid "and ensuring that only the local URL exists in post excerpts."
295
+ msgstr ""
296
+
297
+ #: classes/upgrades/upgrade-filter-post-excerpt.php:45
298
  #, php-format
299
+ msgid ""
300
+ "<strong>Running Excerpts Upgrade%1$s</strong><br>A find &amp; replace is "
301
+ "running in the background to update URLs in your post excerpts. %2$s"
302
  msgstr ""
303
 
304
+ #: classes/upgrades/upgrade-filter-post.php:388
305
  #, php-format
306
  msgid ""
307
  "<strong>Paused Upgrade</strong><br>The find &amp; replace to update URLs has "
308
  "been paused. %s"
309
  msgstr ""
310
 
311
+ #: classes/upgrades/upgrade-filter-post.php:397
312
  msgid "See our documentation"
313
  msgstr ""
314
 
315
+ #: classes/upgrades/upgrade-filter-post.php:403
316
  #, php-format
317
  msgid ""
318
  "%s for details on why we&#8217;re doing this, why it runs slowly, and how to "
319
  "make it run faster."
320
  msgstr ""
321
 
322
+ #: classes/upgrades/upgrade-meta-wp-error.php:49
323
+ msgid ""
324
+ "and rebuilding the metadata for attachments that may have been corrupted."
325
+ msgstr ""
326
+
327
+ #: classes/upgrades/upgrade-region-meta.php:46
328
+ msgid ""
329
+ "and updating the metadata with the bucket region it is served from. This "
330
+ "will allow us to serve your files from the proper S3 region subdomain <span "
331
+ "style=\"white-space:nowrap;\">(e.g. s3-us-west-2.amazonaws.com)</span>."
332
+ msgstr ""
333
+
334
+ #: classes/upgrades/upgrade.php:405
335
  msgid "Pause Update"
336
  msgstr ""
337
 
338
+ #: classes/upgrades/upgrade.php:413
339
  msgid "Restart Update"
340
  msgstr ""
341
 
342
+ #: classes/upgrades/upgrade.php:417
343
  msgid "Try Run It Again"
344
  msgstr ""
345
 
346
+ #: classes/upgrades/upgrade.php:440
347
  #, php-format
348
  msgid ""
349
  "<strong>Running %1$s Update%2$s</strong> &mdash; We&#8217;re going through "
353
  "performance."
354
  msgstr ""
355
 
356
+ #: classes/upgrades/upgrade.php:454
357
  #, php-format
358
  msgid ""
359
  "<strong>%1$s Update Paused%2$s</strong> &mdash; Updating Media Library %3$s "
360
  "has been paused."
361
  msgstr ""
362
 
363
+ #: classes/upgrades/upgrade.php:467
364
  #, php-format
365
  msgid ""
366
  "<strong>Error Updating %1$s</strong> &mdash; We ran into some errors "
368
  "been uploaded to S3. Please check your error log for details. (#%3$d)"
369
  msgstr ""
370
 
371
+ #: classes/upgrades/upgrade.php:491
372
  #, php-format
373
  msgid " (%s%% Complete)"
374
  msgstr ""
375
 
376
+ #: classes/upgrades/upgrade.php:625
377
  #, php-format
378
  msgid "Every %d Minutes"
379
  msgstr ""
380
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  #: classes/wp-aws-compatibility-check.php:323
382
  msgid "deactivate"
383
  msgstr ""
555
  msgid "Change"
556
  msgstr ""
557
 
558
+ #: view/bucket-setting.php:35
559
+ msgid ""
560
+ "<strong>Bucket Select Disabled</strong> &mdash; Bucket selection has been "
561
+ "disabled while files are copied between buckets."
562
+ msgstr ""
563
+
564
  #: view/cloudfront-setting.php:7
565
  #: view/deprecated-domain-setting.php:49
566
  msgid "Invalid character. Letters, numbers, periods and hyphens are allowed."
716
  msgid "Save Changes"
717
  msgstr ""
718
 
719
+ #: view/sidebar.php:13
720
  msgid "Upload existing Media Library to S3"
721
  msgstr ""
722
 
723
+ #: view/sidebar.php:14
724
  msgid "Manage S3 files in WordPress"
725
  msgstr ""
726
 
727
+ #: view/sidebar.php:15
728
  msgid "Assets addon - Serve your CSS & JS from S3/CloudFront"
729
  msgstr ""
730
 
731
+ #: view/sidebar.php:16
732
  msgid "WooCommerce addon"
733
  msgstr ""
734
 
735
+ #: view/sidebar.php:17
736
  msgid "Easy Digital Downloads addon"
737
  msgstr ""
738
 
739
+ #: view/sidebar.php:18
740
  msgid "PriorityExpert™ email support"
741
  msgstr ""
742
 
743
+ #: view/sidebar.php:25
744
  msgid "Visit deliciousbrains.com &rarr;"
745
  msgstr ""
746
 
747
+ #: view/sidebar.php:33
748
  msgid "Get 20% Off!"
749
  msgstr ""
750
 
751
+ #: view/sidebar.php:36
752
  #, php-format
753
  msgid ""
754
  "Submit your name and email and we’ll send you a coupon for 20% off your "
755
  "upgrade."
756
  msgstr ""
757
 
758
+ #: view/sidebar.php:40
759
  msgid "Your Email"
760
  msgstr ""
761
 
762
+ #: view/sidebar.php:44
763
  msgid "First Name"
764
  msgstr ""
765
 
766
+ #: view/sidebar.php:48
767
  msgid "Last Name"
768
  msgstr ""
769
 
770
+ #: view/sidebar.php:55
771
  msgid "Send me the coupon"
772
  msgstr ""
773
 
774
+ #: view/sidebar.php:59
775
  msgid ""
776
  "We promise we will not use your email for anything else and you can "
777
  "unsubscribe with 1-click anytime."
789
  "to a few weeks and will likely be from a non-developer."
790
  msgstr ""
791
 
792
+ #: view/wordpress-org-support.php:10
793
  #, php-format
794
  msgid ""
795
  "If you want a <strong>timely response via email from a developer</strong> "
796
  "who works on this plugin, <a href=\"%s\">upgrade</a> and send us an email."
797
  msgstr ""
798
 
799
+ #: view/wordpress-org-support.php:12
800
  #, php-format
801
  msgid ""
802
  "If you've found a bug, please <a href=\"%s\">submit an issue on GitHub</a>."
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === WP Offload S3 Lite ===
2
  Contributors: bradt, deliciousbrains
3
  Tags: uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
- Requires at least: 4.4
5
- Tested up to: 4.7.3
6
- Stable tag: 1.1.6
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.
@@ -22,12 +22,12 @@ If you're adding this plugin to a site that's been around for a while, your exis
22
 
23
  * Upload existing Media Library to Amazon S3
24
  * Control Amazon S3 files from the Media Library
25
- * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#assets-addon) - Serve your CSS & JS from Amazon S3/CloudFront
26
- * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#woocommerce-addon)
27
- * [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)
28
  * PriorityExpert&trade; email support
29
 
30
- [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)
31
 
32
  The video below runs through the pro upgrade features...
33
 
@@ -48,7 +48,7 @@ which is a fork of [Amazon S3 for WordPress](http://wordpress.org/extend/plugins
48
 
49
  = What are the minimum requirements? =
50
 
51
- You can see the minimum requirements [here](https://deliciousbrains.com/wp-offload-s3/pricing/?utm_source=wordpress.org&utm_medium=web&utm_content=desc&utm_campaign=os3-free-plugin#requirements).
52
 
53
  == Screenshots ==
54
 
@@ -65,6 +65,17 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
65
 
66
  == Changelog ==
67
 
 
 
 
 
 
 
 
 
 
 
 
68
  = WP Offload S3 Lite 1.1.6 - 2017-03-13 =
69
  * New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/)
70
  * New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content
@@ -114,7 +125,7 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
114
 
115
  = WP Offload S3 Lite 1.0.5 - 2016-09-01 =
116
  * New: Compatibility with WordPress 4.6
117
- * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/)
118
 
119
  = WP Offload S3 Lite 1.0.4 - 2016-05-30 =
120
  * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
@@ -220,8 +231,8 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
220
  * Bug fix: Accidentally released the sidebar for after we launch the pro version
221
 
222
  = WP Offload S3 0.9.1 - 2015-07-29 =
223
- * Improvement: Access denied sample IAM policy replaced with link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/)
224
- * Improvement: Access denied messages on bucket selection or bucket creation now link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/)
225
  * Improvement: Object expires time can now be filtered using the `as3cf_object_meta` filter
226
  * Bug fix: Error not always shown when S3 bucket inaccessible due to incorrect permissions
227
  * Bug fix: Permission checks fail when S3 bucket is in a non-default region and defined by `AS3CF_BUCKET` constant
1
  === WP Offload S3 Lite ===
2
  Contributors: bradt, deliciousbrains
3
  Tags: uploads, amazon, s3, amazon s3, mirror, admin, media, cdn, cloudfront
4
+ Requires at least: 4.6
5
+ Tested up to: 4.8
6
+ Stable tag: 1.2
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.
22
 
23
  * Upload existing Media Library to Amazon S3
24
  * Control Amazon S3 files from the Media Library
25
+ * [Assets addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=assets%2Baddon#addons) - Serve your CSS & JS from Amazon S3/CloudFront
26
+ * [WooCommerce addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=woocommerce%2Baddon#addons)
27
+ * [Easy Digital Downloads addon](https://deliciousbrains.com/wp-offload-s3/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=edd%2Baddon#addons)
28
  * PriorityExpert&trade; email support
29
 
30
+ [Compare pro vs free &rarr;](https://deliciousbrains.com/wp-offload-s3/upgrade/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
31
 
32
  The video below runs through the pro upgrade features...
33
 
48
 
49
  = What are the minimum requirements? =
50
 
51
+ You can see the minimum requirements [here](https://deliciousbrains.com/wp-offload-s3/pricing/?utm_campaign=WP%2BOffload%2BS3&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting&utm_content=requirements#requirements).
52
 
53
  == Screenshots ==
54
 
65
 
66
  == Changelog ==
67
 
68
+ = WP Offload S3 Lite 1.2 - 2017-06-19 =
69
+ * New: Compatibility with WordPress 4.8
70
+ * New: Support for WP CLI `wp media regenerate`
71
+ * Improvement: Intermediate image sizes are now passed through the `as3cf_object_meta` filter
72
+ * Improvement: Content filtering cache now uses the external object when available
73
+ * Bug fix: Timeouts on large multisite installs due to excessive database queries on upgrade routines
74
+ * Bug fix: Video files with private ACL not working with WordPress's default media player
75
+ * Bug fix: Bucket permissions check not using configured path
76
+ * Bug fix: WordPress image editor sometimes shows a 404 when 'Remove Files From Server' enabled
77
+ * Bug fix: Notice: Undefined index: region
78
+
79
  = WP Offload S3 Lite 1.1.6 - 2017-03-13 =
80
  * New: Compatibility with [Advanced Custom Fields](https://wordpress.org/plugins/advanced-custom-fields/)
81
  * New: `as3cf_filter_post_local_to_s3` and `as3cf_filter_post_s3_to_local` filters added for filtering S3 URLs in custom content
125
 
126
  = WP Offload S3 Lite 1.0.5 - 2016-09-01 =
127
  * New: Compatibility with WordPress 4.6
128
+ * Improvement: No longer delete plugin data on uninstall. Manual removal possible, as per this [doc](https://deliciousbrains.com/wp-offload-s3/doc/uninstall/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
129
 
130
  = WP Offload S3 Lite 1.0.4 - 2016-05-30 =
131
  * New: Now using simpler Force HTTPS setting, removed redundant Always Use HTTP setting
231
  * Bug fix: Accidentally released the sidebar for after we launch the pro version
232
 
233
  = WP Offload S3 0.9.1 - 2015-07-29 =
234
+ * Improvement: Access denied sample IAM policy replaced with link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
235
+ * Improvement: Access denied messages on bucket selection or bucket creation now link to [Quick Start Guide](https://deliciousbrains.com/wp-offload-s3/doc/quick-start-guide/?utm_campaign=changelogs&utm_source=wordpress.org&utm_medium=free%2Bplugin%2Blisting)
236
  * Improvement: Object expires time can now be filtered using the `as3cf_object_meta` filter
237
  * Bug fix: Error not always shown when S3 bucket inaccessible due to incorrect permissions
238
  * Bug fix: Permission checks fail when S3 bucket is in a non-default region and defined by `AS3CF_BUCKET` constant
view/bucket-setting.php CHANGED
@@ -30,6 +30,14 @@ $tr_class = ( isset( $tr_class ) ) ? $tr_class : '';
30
  if ( isset( $after_bucket_content ) ) {
31
  echo $after_bucket_content;
32
  }
33
- ?>
 
 
 
 
 
 
 
 
34
  </td>
35
  </tr>
30
  if ( isset( $after_bucket_content ) ) {
31
  echo $after_bucket_content;
32
  }
33
+
34
+ $lock_bucket_args = array(
35
+ 'message' => __( '<strong>Bucket Select Disabled</strong> &mdash; Bucket selection has been disabled while files are copied between buckets.', 'amazon-s3-and-cloudfront' ),
36
+ 'id' => 'as3cf-bucket-select-locked',
37
+ 'inline' => true,
38
+ 'type' => 'notice-warning',
39
+ 'style' => 'display: none',
40
+ );
41
+ $this->render_view( 'notice', $lock_bucket_args ); ?>
42
  </td>
43
  </tr>
view/domain-setting.php CHANGED
@@ -19,7 +19,7 @@ if ( $this->show_deprecated_domain_setting( $domain ) ) {
19
  <h4><?php _e( 'CloudFront or Custom Domain', 'amazon-s3-and-cloudfront' ) ?></h4>
20
  <p class="domain-desc">
21
  <?php _e( 'Replace the default S3 domain and path with your CloudFront domain or any domain.', 'amazon-s3-and-cloudfront' ); ?>
22
- <?php echo $this->settings_more_info_link( 'domain' ); ?>
23
  </p>
24
  <?php
25
  $args = $this->get_setting_args( 'cloudfront' );
19
  <h4><?php _e( 'CloudFront or Custom Domain', 'amazon-s3-and-cloudfront' ) ?></h4>
20
  <p class="domain-desc">
21
  <?php _e( 'Replace the default S3 domain and path with your CloudFront domain or any domain.', 'amazon-s3-and-cloudfront' ); ?>
22
+ <?php echo $this->settings_more_info_link( 'domain', 'media+cloudfront+or+custom+domain' ); ?>
23
  </p>
24
  <?php
25
  $args = $this->get_setting_args( 'cloudfront' );
view/settings-tabs.php CHANGED
@@ -1,8 +1,7 @@
1
  <h2 class="nav-tab-wrapper">
2
  <?php
3
- $tabs = $this->get_settings_tabs();
4
- foreach ( $tabs as $tab => $label ) : ?>
5
- <a href="#" class="nav-tab <?php echo 'media' == $tab ? 'nav-tab-active' : ''; ?> js-action-link <?php echo $tab; ?>" data-tab="<?php echo $tab; ?>">
6
  <?php echo esc_html( $label ); ?>
7
  </a>
8
  <?php endforeach; ?>
1
  <h2 class="nav-tab-wrapper">
2
  <?php
3
+ foreach ( $this->get_settings_tabs() as $tab => $label ) : ?>
4
+ <a href="#<?php echo $tab; ?>" class="nav-tab js-action-link <?php echo $tab; ?>" data-tab="<?php echo $tab; ?>">
 
5
  <?php echo esc_html( $label ); ?>
6
  </a>
7
  <?php endforeach; ?>
view/settings.php CHANGED
@@ -56,7 +56,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
56
  <h4><?php _e( 'Copy Files to S3', 'amazon-s3-and-cloudfront' ) ?></h4>
57
  <p>
58
  <?php _e( 'When a file is uploaded to the Media Library, copy it to S3. Existing files are <em>not</em> copied to S3.', 'amazon-s3-and-cloudfront' ); ?>
59
- <?php echo $this->settings_more_info_link( 'copy-to-s3' ); ?>
60
  </p>
61
 
62
  </td>
@@ -71,7 +71,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
71
  <h4><?php _e( 'Rewrite File URLs', 'amazon-s3-and-cloudfront' ) ?></h4>
72
  <p>
73
  <?php _e( 'For Media Library files that have been copied to S3, rewrite the URLs so that they are served from S3/CloudFront instead of your server.', 'amazon-s3-and-cloudfront' ); ?>
74
- <?php echo $this->settings_more_info_link( 'serve-from-s3' ); ?>
75
  </p>
76
 
77
  </td>
@@ -101,7 +101,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
101
  <h4><?php _e( 'Path', 'amazon-s3-and-cloudfront' ) ?></h4>
102
  <p class="object-prefix-desc">
103
  <?php _e( 'By default the path is the same as your local WordPress files.', 'amazon-s3-and-cloudfront' ); ?>
104
- <?php echo $this->settings_more_info_link( 'object-prefix' ); ?>
105
  </p>
106
  <p class="as3cf-setting <?php echo $prefix; ?>-enable-object-prefix <?php echo ( $this->get_setting( 'enable-object-prefix' ) ) ? '' : 'hide'; // xss ok ?>">
107
  <?php $args = $this->get_setting_args( 'object-prefix' ); ?>
@@ -120,7 +120,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
120
  <h4><?php _e( 'Year/Month', 'amazon-s3-and-cloudfront' ) ?></h4>
121
  <p>
122
  <?php _e( 'Add the Year/Month in the URL.', 'amazon-s3-and-cloudfront' ); ?>
123
- <?php echo $this->settings_more_info_link( 'use-yearmonth-folders' ); ?>
124
  </p>
125
  </td>
126
  </tr>
@@ -135,7 +135,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
135
  <h4><?php _e( 'Force HTTPS', 'amazon-s3-and-cloudfront' ) ?></h4>
136
  <p>
137
  <?php _e( 'By default we use HTTPS when the request is HTTPS and regular HTTP when the request is HTTP, but you may want to force the use of HTTPS always, regardless of the request.', 'amazon-s3-and-cloudfront' ); ?>
138
- <?php echo $this->settings_more_info_link( 'force-https' ); ?>
139
  </p>
140
  </td>
141
  </tr>
@@ -152,7 +152,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
152
  <?php echo $args['setting_msg']; ?>
153
  <h4><?php _e( 'Remove Files From Server', 'amazon-s3-and-cloudfront' ) ?></h4>
154
  <p><?php _e( 'Once a file has been copied to S3, remove it from the local server.', 'amazon-s3-and-cloudfront' ); ?>
155
- <?php echo $this->settings_more_info_link( 'remove-local-file' ); ?>
156
  </p>
157
  <?php
158
  $lost_files_msg = apply_filters( 'as3cf_lost_files_notice', __( '<strong>Broken URLs</strong> &mdash; There will be broken URLs for files that don\'t exist locally. You can fix this by enabling <strong>Rewrite File URLs</strong> to use the S3 URLs.', 'amazon-s3-and-cloudfront' ) );
@@ -165,7 +165,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
165
  );
166
  $this->render_view( 'notice', $lost_files_args );
167
 
168
- $remove_local_link = $this->more_info_link( 'https://deliciousbrains.com/wp-offload-s3/doc/compatibility-with-other-plugins/' );
169
  $remove_local_msg = apply_filters( 'as3cf_remove_local_notice', sprintf( __( '<strong>Warning</strong> &mdash; Some plugins depend on the file being present on the local server and may not work when the file is removed. %s', 'amazon-s3-and-cloudfront' ), $remove_local_link ) );
170
  $remove_local_args = array(
171
  'message' => $remove_local_msg,
@@ -187,7 +187,7 @@ $selected_bucket_prefix = $this->get_object_prefix(); ?>
187
  <h4><?php _e( 'Object Versioning', 'amazon-s3-and-cloudfront' ) ?></h4>
188
  <p>
189
  <?php _e( 'Append a timestamp to the S3 file path. Recommended when using CloudFront so you don\'t have to worry about cache invalidation.', 'amazon-s3-and-cloudfront' ); ?>
190
- <?php echo $this->settings_more_info_link( 'object-versioning' ); ?>
191
  </p>
192
  </td>
193
  </tr>
56
  <h4><?php _e( 'Copy Files to S3', 'amazon-s3-and-cloudfront' ) ?></h4>
57
  <p>
58
  <?php _e( 'When a file is uploaded to the Media Library, copy it to S3. Existing files are <em>not</em> copied to S3.', 'amazon-s3-and-cloudfront' ); ?>
59
+ <?php echo $this->settings_more_info_link( 'copy-to-s3', 'media+copy+files+to+S3' ); ?>
60
  </p>
61
 
62
  </td>
71
  <h4><?php _e( 'Rewrite File URLs', 'amazon-s3-and-cloudfront' ) ?></h4>
72
  <p>
73
  <?php _e( 'For Media Library files that have been copied to S3, rewrite the URLs so that they are served from S3/CloudFront instead of your server.', 'amazon-s3-and-cloudfront' ); ?>
74
+ <?php echo $this->settings_more_info_link( 'serve-from-s3', 'media+rewrite+file+urls' ); ?>
75
  </p>
76
 
77
  </td>
101
  <h4><?php _e( 'Path', 'amazon-s3-and-cloudfront' ) ?></h4>
102
  <p class="object-prefix-desc">
103
  <?php _e( 'By default the path is the same as your local WordPress files.', 'amazon-s3-and-cloudfront' ); ?>
104
+ <?php echo $this->settings_more_info_link( 'object-prefix', 'media+path' ); ?>
105
  </p>
106
  <p class="as3cf-setting <?php echo $prefix; ?>-enable-object-prefix <?php echo ( $this->get_setting( 'enable-object-prefix' ) ) ? '' : 'hide'; // xss ok ?>">
107
  <?php $args = $this->get_setting_args( 'object-prefix' ); ?>
120
  <h4><?php _e( 'Year/Month', 'amazon-s3-and-cloudfront' ) ?></h4>
121
  <p>
122
  <?php _e( 'Add the Year/Month in the URL.', 'amazon-s3-and-cloudfront' ); ?>
123
+ <?php echo $this->settings_more_info_link( 'use-yearmonth-folders', 'media+year+month' ); ?>
124
  </p>
125
  </td>
126
  </tr>
135
  <h4><?php _e( 'Force HTTPS', 'amazon-s3-and-cloudfront' ) ?></h4>
136
  <p>
137
  <?php _e( 'By default we use HTTPS when the request is HTTPS and regular HTTP when the request is HTTP, but you may want to force the use of HTTPS always, regardless of the request.', 'amazon-s3-and-cloudfront' ); ?>
138
+ <?php echo $this->settings_more_info_link( 'force-https', 'media+force+https' ); ?>
139
  </p>
140
  </td>
141
  </tr>
152
  <?php echo $args['setting_msg']; ?>
153
  <h4><?php _e( 'Remove Files From Server', 'amazon-s3-and-cloudfront' ) ?></h4>
154
  <p><?php _e( 'Once a file has been copied to S3, remove it from the local server.', 'amazon-s3-and-cloudfront' ); ?>
155
+ <?php echo $this->settings_more_info_link( 'remove-local-file', 'media+remove+files+from+server' ); ?>
156
  </p>
157
  <?php
158
  $lost_files_msg = apply_filters( 'as3cf_lost_files_notice', __( '<strong>Broken URLs</strong> &mdash; There will be broken URLs for files that don\'t exist locally. You can fix this by enabling <strong>Rewrite File URLs</strong> to use the S3 URLs.', 'amazon-s3-and-cloudfront' ) );
165
  );
166
  $this->render_view( 'notice', $lost_files_args );
167
 
168
+ $remove_local_link = $this->more_info_link( '/wp-offload-s3/doc/compatibility-with-other-plugins/', 'error-media+remove+files+from+server' );
169
  $remove_local_msg = apply_filters( 'as3cf_remove_local_notice', sprintf( __( '<strong>Warning</strong> &mdash; Some plugins depend on the file being present on the local server and may not work when the file is removed. %s', 'amazon-s3-and-cloudfront' ), $remove_local_link ) );
170
  $remove_local_args = array(
171
  'message' => $remove_local_msg,
187
  <h4><?php _e( 'Object Versioning', 'amazon-s3-and-cloudfront' ) ?></h4>
188
  <p>
189
  <?php _e( 'Append a timestamp to the S3 file path. Recommended when using CloudFront so you don\'t have to worry about cache invalidation.', 'amazon-s3-and-cloudfront' ); ?>
190
+ <?php echo $this->settings_more_info_link( 'object-versioning', 'media+object+versioning' ); ?>
191
  </p>
192
  </td>
193
  </tr>
view/sidebar.php CHANGED
@@ -1,6 +1,9 @@
1
  <div class="as3cf-sidebar">
2
 
3
- <a class="as3cf-banner" href="https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&amp;utm_medium=web&amp;utm_content=sidebar&amp;utm_campaign=os3-free-plugin">
 
 
 
4
  <h1>Upgrade</h1>
5
  </a>
6
 
@@ -15,11 +18,16 @@
15
  <li><?php echo wptexturize( __( 'PriorityExpert™ email support', 'amazon-s3-and-cloudfront' ) ); // xss ok ?></li>
16
  </ul>
17
 
18
- <p><a href="https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&amp;utm_medium=web&amp;utm_content=sidebar&amp;utm_campaign=os3-free-plugin"><?php echo __( 'Visit deliciousbrains.com &rarr;', 'amazon-s3-and-cloudfront' ); ?></a></p>
 
 
 
 
 
19
 
20
  </div>
21
 
22
- <form method="post" action="https://deliciousbrains.com/email-subscribe/" target="_blank" class="subscribe block">
23
  <?php $user = wp_get_current_user(); ?>
24
 
25
  <h2><?php _e( 'Get 20% Off!', 'amazon-s3-and-cloudfront' ); ?></h2>
@@ -56,7 +64,10 @@
56
  <h4>Created &amp; maintained by</h4>
57
  <ul>
58
  <li>
59
- <a href="https://deliciousbrains.com/?utm_source=insideplugin&amp;utm_medium=web&amp;utm_content=sidebar&amp;utm_campaign=os3-free-plugin">
 
 
 
60
  <img src="//www.gravatar.com/avatar/e62fc2e9c8d9fc6edd4fea5339036a91?size=64" alt="" width="32" height="32">
61
  <span>Delicious Brains Inc.</span>
62
  </a>
1
  <div class="as3cf-sidebar">
2
 
3
+ <a class="as3cf-banner" href="<?php echo $this->dbrains_url( '/wp-offload-s3/', array(
4
+ 'utm_campaign' => 'WP+Offload+S3',
5
+ 'utm_content' => 'sidebar',
6
+ ) ); ?>">
7
  <h1>Upgrade</h1>
8
  </a>
9
 
18
  <li><?php echo wptexturize( __( 'PriorityExpert™ email support', 'amazon-s3-and-cloudfront' ) ); // xss ok ?></li>
19
  </ul>
20
 
21
+ <p>
22
+ <a href="<?php echo $this->dbrains_url( '/wp-offload-s3/', array(
23
+ 'utm_campaign' => 'WP+Offload+S3',
24
+ 'utm_content' => 'sidebar',
25
+ ) ); ?>"><?php echo __( 'Visit deliciousbrains.com &rarr;', 'amazon-s3-and-cloudfront' ); ?></a>
26
+ </p>
27
 
28
  </div>
29
 
30
+ <form method="post" action="<?php echo Amazon_S3_And_CloudFront::DBRAINS_URL ?>/email-subscribe/" target="_blank" class="subscribe block">
31
  <?php $user = wp_get_current_user(); ?>
32
 
33
  <h2><?php _e( 'Get 20% Off!', 'amazon-s3-and-cloudfront' ); ?></h2>
64
  <h4>Created &amp; maintained by</h4>
65
  <ul>
66
  <li>
67
+ <a href="<?php echo $this->dbrains_url( '', array(
68
+ 'utm_campaign' => 'WP+Offload+S3',
69
+ 'utm_content' => 'sidebar',
70
+ ) ); ?>">
71
  <img src="//www.gravatar.com/avatar/e62fc2e9c8d9fc6edd4fea5339036a91?size=64" alt="" width="32" height="32">
72
  <span>Delicious Brains Inc.</span>
73
  </a>
view/wordpress-org-support.php CHANGED
@@ -3,7 +3,11 @@
3
 
4
  <p><?php printf( __( 'You may ask the WordPress community for help by posting to the <a href="%s">WordPress.org support forum</a>. Response time can range from a few days to a few weeks and will likely be from a non-developer.', 'amazon-s3-and-cloudfront'), 'https://wordpress.org/plugins/amazon-s3-and-cloudfront/' ); ?></p>
5
 
6
- <p class="upgrade-to-pro"><?php printf( __( 'If you want a <strong>timely response via email from a developer</strong> who works on this plugin, <a href="%s">upgrade</a> and send us an email.', 'amazon-s3-and-cloudfront' ), 'https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&utm_medium=web&utm_content=support-tab&utm_campaign=os3-free-plugin' ); ?></p>
 
 
 
 
7
 
8
  <p><?php printf( __( 'If you\'ve found a bug, please <a href="%s">submit an issue on GitHub</a>.', 'amazon-s3-and-cloudfront' ), 'https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/issues' ); ?></p>
9
 
3
 
4
  <p><?php printf( __( 'You may ask the WordPress community for help by posting to the <a href="%s">WordPress.org support forum</a>. Response time can range from a few days to a few weeks and will likely be from a non-developer.', 'amazon-s3-and-cloudfront'), 'https://wordpress.org/plugins/amazon-s3-and-cloudfront/' ); ?></p>
5
 
6
+ <?php $url = $this->dbrains_url( '/wp-offload-s3/', array(
7
+ 'utm_campaign' => 'WP+Offload+S3',
8
+ 'utm_content' => 'support+tab',
9
+ ) ); ?>
10
+ <p class="upgrade-to-pro"><?php printf( __( 'If you want a <strong>timely response via email from a developer</strong> who works on this plugin, <a href="%s">upgrade</a> and send us an email.', 'amazon-s3-and-cloudfront' ), $url ); ?></p>
11
 
12
  <p><?php printf( __( 'If you\'ve found a bug, please <a href="%s">submit an issue on GitHub</a>.', 'amazon-s3-and-cloudfront' ), 'https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/issues' ); ?></p>
13
 
wordpress-s3.php CHANGED
@@ -4,8 +4,8 @@ Plugin Name: WP Offload S3 Lite
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: 1.1.6
8
- Author URI: http://deliciousbrains.com/
9
  Network: True
10
  Text Domain: amazon-s3-and-cloudfront
11
  Domain Path: /languages/
@@ -26,9 +26,9 @@ Domain Path: /languages/
26
  // Then completely rewritten.
27
  */
28
 
29
- $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '1.1.6';
30
 
31
- $aws_plugin_version_required = '1.0.1';
32
 
33
  require_once dirname( __FILE__ ) . '/classes/wp-aws-compatibility-check.php';
34
  require_once dirname( __FILE__ ) . '/classes/as3cf-utils.php';
@@ -61,14 +61,6 @@ function as3cf_init( $aws ) {
61
  $abspath = dirname( __FILE__ );
62
  require_once $abspath . '/include/functions.php';
63
  require_once $abspath . '/classes/as3cf-error.php';
64
- require_once $abspath . '/classes/as3cf-upgrade.php';
65
- require_once $abspath . '/classes/as3cf-upgrade-filter-post.php';
66
- require_once $abspath . '/classes/upgrades/as3cf-region-meta.php';
67
- require_once $abspath . '/classes/upgrades/as3cf-file-sizes.php';
68
- require_once $abspath . '/classes/upgrades/as3cf-meta-wp-error.php';
69
- require_once $abspath . '/classes/upgrades/as3cf-filter-edd.php';
70
- require_once $abspath . '/classes/upgrades/as3cf-filter-post-content.php';
71
- require_once $abspath . '/classes/upgrades/as3cf-filter-post-excerpt.php';
72
  require_once $abspath . '/classes/as3cf-filter.php';
73
  require_once $abspath . '/classes/filters/as3cf-local-to-s3.php';
74
  require_once $abspath . '/classes/filters/as3cf-s3-to-local.php';
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: 1.2
8
+ Author URI: https://deliciousbrains.com/
9
  Network: True
10
  Text Domain: amazon-s3-and-cloudfront
11
  Domain Path: /languages/
26
  // Then completely rewritten.
27
  */
28
 
29
+ $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '1.2';
30
 
31
+ $aws_plugin_version_required = '1.0.3';
32
 
33
  require_once dirname( __FILE__ ) . '/classes/wp-aws-compatibility-check.php';
34
  require_once dirname( __FILE__ ) . '/classes/as3cf-utils.php';
61
  $abspath = dirname( __FILE__ );
62
  require_once $abspath . '/include/functions.php';
63
  require_once $abspath . '/classes/as3cf-error.php';
 
 
 
 
 
 
 
 
64
  require_once $abspath . '/classes/as3cf-filter.php';
65
  require_once $abspath . '/classes/filters/as3cf-local-to-s3.php';
66
  require_once $abspath . '/classes/filters/as3cf-s3-to-local.php';