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 ) {