WP Offload S3 Lite - Version 0.8

Version Description

  • 2015-01-10 =
  • New: Redesigned settings UI
  • Improvement: SSL setting can be fully controlled, HTTPS for urls always, based on request or never
  • Improvement: Download files from S3 that are not found on server when running Regenerate Thumbnails plugin
  • Improvement: When calling get_attached_file() and file is missing from server, return S3 URL
  • Improvement: Code cleanup to WordPress coding standards
  • Bug Fix: Files for all subsites going into the same S3 folder on multisite installs setup prior to WP 3.5
  • Bug Fix: 'attempting to access local file system' error for some installs
Download this release

Release Info

Developer bradt
Plugin Icon 128x128 WP Offload S3 Lite
Version 0.8
Comparing to
See all releases

Code changes from version 0.7.2 to 0.8

CONTRIBUTING.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Contributing
2
+
3
+ ### Getting Started
4
+
5
+ Submit a ticket for your issue, assuming one does not already exist.
6
+ * Raise it on our [Issue Tracker](https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront)
7
+ * Clearly describe the issue, including steps to reproduce the bug (if applicable).
8
+ * If it's a bug, make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using.
9
+
10
+ ## Making Changes
11
+
12
+ * Fork the repository on GitHub
13
+ * From the `develop` branch on your forked repository, create a new branch and make your changes
14
+ * It is suggested that your new branch use a name that briefly describes the feature or issue.
15
+ * Ensure you stick to the [WordPress Coding Standards](http://codex.wordpress.org/WordPress_Coding_Standards)
16
+ * When committing, use a [well-formed](http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message) [commit](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [message](http://who-t.blogspot.com/2009/12/on-commit-messages.html)
17
+ * Push the changes to your fork and submit a pull request to the `develop` branch of the plugin's repository
18
+
19
+ ## Code Documentation
20
+
21
+ * Code comments should be added to all new functions/methods.
22
+ * Comments should tell you the "what" & "why". You'll typically want a one-liner that says what it does, hopefully why, but not how. Only very rarely should they tell you how (e.g. when the code is necessarily complex).
23
+ * Also see the [WordPress PHP Documentation Standards](http://make.wordpress.org/core/handbook/inline-documentation-standards/php-documentation-standards/) doc for general guidelines and best practices.
24
+ * We currently suggest implementing the `@param` & `@return` PHPdoc tags for every function/method if applicable.
25
+
26
+ At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary.
27
+
28
+ # Additional Resources
29
+ * [GitHub Help — Forking](https://help.github.com/articles/fork-a-repo)
30
+ * [GitHub Help — Syncing a Fork](https://help.github.com/articles/syncing-a-fork)
31
+ * [GitHub Help — Pull Requests](https://help.github.com/articles/using-pull-requests#before-you-begin)
assets/Gruntfile.js CHANGED
@@ -17,7 +17,7 @@ module.exports = function(grunt) {
17
  },
18
  watch: {
19
  js: {
20
- files: ['js/*'],
21
  tasks: ['uglify']
22
  },
23
  sass: {
17
  },
18
  watch: {
19
  js: {
20
+ files: ['js/*.js', '!js/*.min.js'],
21
  tasks: ['uglify']
22
  },
23
  sass: {
assets/css/styles.css CHANGED
@@ -1 +1 @@
1
- .aws-main .error{max-width:835px}.aws-main .error pre{background:#eaeaea;background:rgba(0,0,0,0.07);display:block;padding:10px 15px}.aws-main .error pre code{padding:0;background:none}.aws-main .as3cf-notice{max-width:835px}.as3cf-settings{position:relative;width:550px;min-height:800px}.as3cf-settings select.bucket{margin-bottom:5px;width:380px}.as3cf-settings .form-table td{padding-left:0;padding-right:0}.as3cf-settings .form-table tr:first-child h3{margin-top:0}.as3cf-settings .tooltip{position:relative;z-index:2;cursor:pointer}.as3cf-settings .tooltip:before,.as3cf-settings .tooltip:after{visibility:hidden;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0;pointer-events:none}.as3cf-settings .tooltip:before{position:absolute;bottom:150%;left:50%;margin-bottom:5px;margin-left:-250px;padding:10px;width:500px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background-color:#000;background-color:rgba(51,51,51,0.9);color:#fff;content:attr(data-tooltip);text-align:center;font-size:14px;line-height:1.3}.as3cf-settings .tooltip:after{position:absolute;bottom:150%;left:50%;margin-left:-5px;width:0;border-top:5px solid #000;border-top:5px solid rgba(51,51,51,0.9);border-right:5px solid transparent;border-left:5px solid transparent;content:" ";font-size:0;line-height:0}.as3cf-settings .tooltip:hover:before,.as3cf-settings .tooltip:hover:after{visibility:visible;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}.as3cf-banner img{display:block}.as3cf-sidebar{position:absolute;top:17px;right:-312px;width:292px}.as3cf-sidebar .block{padding:20px;border:1px solid #ccc}.as3cf-sidebar .subscribe{border-top:none}.as3cf-sidebar .subscribe h2{padding:0;margin:0;margin-bottom:0.5em;color:#666;font-size:20px;line-height:1.2em;float:none}.as3cf-sidebar .subscribe h3{font-size:16px;margin:0}.as3cf-sidebar .subscribe p{margin:0}.as3cf-sidebar .subscribe .intro{margin-bottom:1em;line-height:1.4}.as3cf-sidebar .subscribe ul{margin-left:20px;list-style-type:disc}.as3cf-sidebar .subscribe li{line-height:1.4}.as3cf-sidebar .subscribe .links{margin-bottom:2em}.as3cf-sidebar .subscribe .links a{text-decoration:none}.as3cf-sidebar .subscribe .promise{color:#999;font-size:12px;line-height:1.4em}.as3cf-sidebar .subscribe .field{margin-bottom:0.5em}.as3cf-sidebar .subscribe .field p{margin-bottom:0.3em}.as3cf-sidebar .subscribe .field.submit-button{margin-bottom:1em}.as3cf-sidebar .credits{border-top:0}.as3cf-sidebar .credits h4{font-size:16px;margin-top:0;margin-bottom:10px}.as3cf-sidebar .credits ul{margin:0}.as3cf-sidebar .credits li{overflow:hidden}.as3cf-sidebar .credits li:last-child{margin-bottom:0}.as3cf-sidebar .credits img{float:left;margin-right:10px}.as3cf-sidebar .credits span{float:left;display:block;line-height:32px}.as3cf-sidebar .credits a{display:block;text-decoration:none;color:#444;font-size:16px;text-align:center}.as3cf-sidebar .credits a:hover{color:#888}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.as3cf-sidebar .as3cf-banner{background-image:url(../img/snail@2x.jpg);background-size:292px 165px;width:292px;height:165px;display:block}.as3cf-sidebar .as3cf-banner img{display:none}}@media screen and (max-width: 1052px){.as3cf-sidebar{position:relative;top:auto;right:auto;margin-top:50px}}
1
+ .aws-main .error pre{background:#eaeaea;background:rgba(0,0,0,0.07);display:block;padding:10px 15px}.aws-main .error pre code{padding:0;background:none}.aws-main .as3cf-notice,.aws-main .error{max-width:935px}.aws-main .updated{display:none}.aws-main .updated.show{display:block}.as3cf-settings{position:relative;width:650px;min-height:800px}.as3cf-settings .as3cf-main-settings{display:none}.as3cf-settings .as3cf-main-settings p{font-size:13px}.as3cf-settings .as3cf-main-settings p a{color:#444}.as3cf-settings.as3cf-has-bucket .as3cf-bucket-select{display:none}.as3cf-settings.as3cf-has-bucket .as3cf-main-settings{display:block}.as3cf-settings tr.configure-url,.as3cf-settings tr.advanced-options{display:none}.as3cf-settings .object-prefix-desc em{white-space:nowrap}.as3cf-settings .as3cf-url-preview-wrap{background:#ffffff;text-align:center;padding:20px 20px 10px;max-width:610px;width:100%}.as3cf-settings .as3cf-url-preview-wrap .as3cf-url-preview{margin-top:5px;overflow-x:scroll;padding-bottom:15px}.as3cf-settings .as3cf-url-preview-wrap span{color:#aaa;text-transform:uppercase;font-weight:bold}.as3cf-settings .as3cf-ssl p.info{margin-top:10px;padding:0}.as3cf-settings .as3cf-radio-group label{display:block;margin-bottom:10px}.as3cf-settings .as3cf-radio-group label.disabled,.as3cf-settings .as3cf-radio-group label.disabled p{color:#bbbbbb;cursor:default}.as3cf-settings .as3cf-radio-group p{padding-left:25px;color:#6b6b6b;margin:0;font-size:12px}.as3cf-settings .as3cf-radio-group p.as3cf-setting{margin-top:5px}.as3cf-settings .as3cf-switch{position:relative}.as3cf-settings .as3cf-switch.disabled span{cursor:default;margin:1px 0 0 0}.as3cf-settings .as3cf-switch.disabled span.checked{background:#F1F1F1;border:1px solid #dddddd;color:#a3a3a3}.as3cf-settings .as3cf-switch span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;float:left;display:inline-block;height:100%;font-size:12px;line-height:20px;border-radius:2px;-webkit-border-radius:2px;font-weight:bold;padding:4px 8px;background:#F1F1F1;border:1px solid #dddddd;color:#CCCCCC;margin:1px -1px 0;z-index:1}.as3cf-settings .as3cf-switch span.checked{background:#ffffff;border-color:#E4E4E4;color:#373737;padding:5px 10px;margin:0;z-index:2}.as3cf-settings .as3cf-switch input[type="checkbox"]{position:absolute !important;top:0;left:0;opacity:0;filter:alpha(opacity=0);z-index:-1}.as3cf-settings .as3cf-setting.hide{display:none}.as3cf-settings .as3cf-bucket-actions{position:absolute;right:0;top:2px}.as3cf-settings .as3cf-bucket-actions .as3cf-cancel-bucket-select-wrap{display:none}.as3cf-settings .as3cf-bucket-actions .as3cf-cancel-bucket-select-wrap:after{content:" | "}.as3cf-settings .as3cf-bucket-list-wrapper{background:#fff;padding:15px 20px;margin-bottom:2em}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list{max-height:300px;overflow:auto;margin:0}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list li{font-size:14px;padding:10px 0}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a{color:#444;text-decoration:none}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a:hover,.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a.selected{color:#0074A2}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a.selected{font-weight:bold}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a .bucket{float:left;clear:both}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a .bucket .dashicons{margin-right:5px}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list a .spinner{float:left;background-size:15px 15px}.as3cf-settings .as3cf-bucket-list-wrapper .as3cf-bucket-list.saving{opacity:0.5}.as3cf-settings .as3cf-create-bucket-form input[type="text"]{width:80%}.as3cf-settings select.bucket{margin-bottom:5px;width:380px}.as3cf-settings .form-table td{padding-left:0;padding-right:0}.as3cf-settings .form-table td:first-child{vertical-align:top;min-width:120px}.as3cf-settings .form-table h3{font-weight:normal;text-transform:uppercase;margin:0}.as3cf-settings .form-table h4{margin:0}.as3cf-settings .form-table .as3cf-border-bottom td{border-bottom:1px solid #ddd}.as3cf-settings .as3cf-active-bucket{font-weight:bold;margin-right:10px}.as3cf-settings .tooltip{position:relative;z-index:2;cursor:pointer}.as3cf-settings .tooltip:before,.as3cf-settings .tooltip:after{visibility:hidden;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=0);opacity:0;pointer-events:none}.as3cf-settings .tooltip:before{position:absolute;bottom:150%;left:50%;margin-bottom:5px;margin-left:-250px;padding:10px;width:500px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background-color:#000;background-color:rgba(51,51,51,0.9);color:#fff;content:attr(data-tooltip);text-align:center;font-size:14px;line-height:1.3}.as3cf-settings .tooltip:after{position:absolute;bottom:150%;left:50%;margin-left:-5px;width:0;border-top:5px solid #000;border-top:5px solid rgba(51,51,51,0.9);border-right:5px solid transparent;border-left:5px solid transparent;content:" ";font-size:0;line-height:0}.as3cf-settings .tooltip:hover:before,.as3cf-settings .tooltip:hover:after{visibility:visible;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}.as3cf-banner img{display:block}.as3cf-sidebar{position:absolute;top:17px;right:-312px;width:292px}.as3cf-sidebar .block{padding:20px;border:1px solid #ccc}.as3cf-sidebar .subscribe{border-top:none}.as3cf-sidebar .subscribe h2{padding:0;margin:0;margin-bottom:0.5em;color:#666;font-size:20px;line-height:1.2em;float:none}.as3cf-sidebar .subscribe h3{font-size:16px;margin:0}.as3cf-sidebar .subscribe p{margin:0}.as3cf-sidebar .subscribe .intro{margin-bottom:1em;line-height:1.4}.as3cf-sidebar .subscribe ul{margin-left:20px;list-style-type:disc}.as3cf-sidebar .subscribe li{line-height:1.4}.as3cf-sidebar .subscribe .links{margin-bottom:2em}.as3cf-sidebar .subscribe .links a{text-decoration:none}.as3cf-sidebar .subscribe .promise{color:#999;font-size:12px;line-height:1.4em}.as3cf-sidebar .subscribe .field{margin-bottom:0.5em}.as3cf-sidebar .subscribe .field p{margin-bottom:0.3em}.as3cf-sidebar .subscribe .field.submit-button{margin-bottom:1em}.as3cf-sidebar .credits{border-top:0}.as3cf-sidebar .credits h4{font-size:16px;margin-top:0;margin-bottom:10px}.as3cf-sidebar .credits ul{margin:0}.as3cf-sidebar .credits li{overflow:hidden}.as3cf-sidebar .credits li:last-child{margin-bottom:0}.as3cf-sidebar .credits img{float:left;margin-right:10px}.as3cf-sidebar .credits span{float:left;display:block;line-height:32px}.as3cf-sidebar .credits a{display:block;text-decoration:none;color:#444;font-size:16px;text-align:center}.as3cf-sidebar .credits a:hover{color:#888}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.as3cf-sidebar .as3cf-banner{background-image:url(../img/snail@2x.jpg);background-size:292px 165px;width:292px;height:165px;display:block}.as3cf-sidebar .as3cf-banner img{display:none}}@media screen and (max-width: 1052px){.as3cf-sidebar{position:relative;top:auto;right:auto;margin-top:50px}}
assets/js/script.js CHANGED
@@ -1,78 +1,316 @@
1
  (function($) {
 
2
 
3
  $(document).ready(function() {
4
 
5
  $('.as3cf-settings').each(function() {
6
  var $container = $(this);
 
 
7
 
8
- $('select.bucket', $container).change(function() {
9
- var $select = $(this);
 
10
 
11
- if ($select.val() !== 'new') {
12
- return;
13
- }
 
 
 
 
14
 
15
- var error_func = function(jqXHR, textStatus, errorThrown) {
16
- alert(as3cf_i18n.create_bucket_error + errorThrown);
17
- $select[0].selectedIndex = 0;
18
- console.log( jqXHR );
19
- console.log( textStatus );
20
- console.log( errorThrown );
21
- };
22
 
23
- var success_func = function(data, textStatus, jqXHR) {
24
- if (typeof data['success'] !== 'undefined') {
25
- var opt = document.createElement('option');
26
- opt.value = opt.innerHTML = bucket_name;
27
- var inserted_at_position = 0;
28
- $('option', $select).each(function() {
29
- // For some reason, no error occurs when
30
- // adding a bucket you've already added
31
- if ($(this).val() == bucket_name) {
32
- return false;
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
- if ($(this).val() > bucket_name || 'new' == $(this).val() ) {
35
- $(opt).insertBefore($(this));
36
- return false;
37
- }
38
- inserted_at_position = inserted_at_position + 1;
39
- });
40
- $select[0].selectedIndex = inserted_at_position;
41
 
42
- // If they decided to create a new bucket before refreshing
43
- // the page, we need another nonce
44
- as3cf_i18n.create_bucket_nonce = data['_nonce'];
 
 
 
 
 
 
 
45
  }
46
- else {
47
- alert(as3cf_i18n.create_bucket_error + data['error']);
48
- $select[0].selectedIndex = 0;
49
  }
50
- };
 
51
 
52
- var bucket_name = window.prompt(as3cf_i18n.create_bucket_prompt);
53
- if (!bucket_name) {
54
- $select[0].selectedIndex = 0;
55
- return;
56
- }
 
 
57
 
58
- var data = {
59
- action: 'as3cf-create-bucket',
60
- bucket_name: bucket_name,
61
- _nonce: as3cf_i18n.create_bucket_nonce
62
- };
63
-
64
- $.ajax({
65
- url: ajaxurl,
66
- type: 'POST',
67
- dataType: 'JSON',
68
- success: success_func,
69
- error: error_func,
70
- data: data
71
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
 
 
 
 
 
74
  });
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  });
77
 
78
  })(jQuery);
1
  (function($) {
2
+ var saved_settings;
3
 
4
  $(document).ready(function() {
5
 
6
  $('.as3cf-settings').each(function() {
7
  var $container = $(this);
8
+ var $bucketList = $('.as3cf-bucket-list');
9
+ var $createBucketForm = $container.find('.as3cf-create-bucket-form');
10
 
11
+ if($createBucketForm.length){
12
+ var $createBucketButton = $createBucketForm.find('button'),
13
+ origButtonText = $createBucketButton.text();
14
 
15
+ $createBucketForm.on('submit', function(e){
16
+ e.preventDefault();
17
+ $( '.as3cf-bucket-error' ).hide();
18
+ $bucketList.addClass('saving');
19
+ $createBucketButton.text($createBucketButton.attr('data-working'));
20
+ $createBucketButton.prop('disabled', true);
21
+ var bucketName = $createBucketForm.find('input[name="bucket_name"]').val();
22
 
23
+ var data = {
24
+ action: 'as3cf-create-bucket',
25
+ bucket_name: bucketName,
26
+ _nonce: as3cf_i18n.create_bucket_nonce
27
+ };
 
 
28
 
29
+ $.ajax({
30
+ url: ajaxurl,
31
+ type: 'POST',
32
+ dataType: 'JSON',
33
+ data: data,
34
+ error: function(jqXHR, textStatus, errorThrown) {
35
+ $createBucketButton.text(origButtonText);
36
+ show_bucket_error( as3cf_i18n.create_bucket_error, errorThrown );
37
+ },
38
+ success: function(data, textStatus, jqXHR) {
39
+ $createBucketButton.text(origButtonText);
40
+ $createBucketButton.prop('disabled', false);
41
+ if (typeof data['success'] !== 'undefined') {
42
+ bucket_select( bucketName, data['region'], data['can_write'] );
43
+ // tidy up create bucket form
44
+ $createBucketForm.find('input[name="bucket_name"]').val('');
45
+ $('.as3cf-bucket-list a' ).removeClass('selected');
46
+ loadBuckets();
47
+ $bucketList.removeClass('saving');
48
+ } else {
49
+ show_bucket_error( as3cf_i18n.create_bucket_error, data['error'] );
50
  }
51
+ }
52
+ });
53
+ });
54
+ }
 
 
 
55
 
56
+ var $changeBucket = $container.find('.as3cf-change-bucket');
57
+ if($changeBucket.length){
58
+ $changeBucket.on('click', function(e){
59
+ e.preventDefault();
60
+ $( '.updated' ).hide();
61
+ $('.as3cf-can-write-error').hide();
62
+ $('.as3cf-settings').removeClass('as3cf-has-bucket');
63
+ loadBuckets();
64
+ if ( $('.as3cf-active-bucket' ).html ) {
65
+ $('.as3cf-cancel-bucket-select-wrap' ).show();
66
  }
67
+ if ( $( '.as3cf-bucket-list a.selected' ).length ) {
68
+ $( '.as3cf-bucket-list' ).scrollTop( $( '.as3cf-bucket-list a.selected' ).position().top - 50 );
 
69
  }
70
+ });
71
+ }
72
 
73
+ var $refreshBuckets = $container.find('.as3cf-refresh-buckets');
74
+ if($refreshBuckets.length){
75
+ $refreshBuckets.on('click', function(e){
76
+ e.preventDefault();
77
+ loadBuckets();
78
+ });
79
+ }
80
 
81
+ var $cancelChangeBucket = $container.find('.as3cf-cancel-bucket-select');
82
+ if($cancelChangeBucket.length){
83
+ $cancelChangeBucket.on('click', function(e){
84
+ e.preventDefault();
85
+ $( '.as3cf-bucket-error' ).hide();
86
+ $('.as3cf-settings').addClass('as3cf-has-bucket');
 
 
 
 
 
 
 
87
  });
88
+ }
89
+
90
+ });
91
+
92
+ var $bucketList = $('.as3cf-bucket-list');
93
+ function loadBuckets() {
94
+ $( '.as3cf-bucket-error' ).hide();
95
+ $bucketList.html('<li class="loading">'+ $bucketList.attr('data-working') +'</li>');
96
+
97
+ var data = {
98
+ action: 'as3cf-get-buckets',
99
+ _nonce: as3cf_i18n.get_buckets_nonce
100
+ };
101
+
102
+ $.ajax({
103
+ url: ajaxurl,
104
+ type: 'POST',
105
+ dataType: 'JSON',
106
+ data: data,
107
+ error: function(jqXHR, textStatus, errorThrown) {
108
+ $bucketList.html('');
109
+ show_bucket_error( as3cf_i18n.get_buckets_error, errorThrown );
110
+ },
111
+ success: function(data, textStatus, jqXHR) {
112
+ $bucketList.html('');
113
+ if (typeof data['success'] !== 'undefined') {
114
+ $(data['buckets']).each(function(idx, bucket){
115
+ var bucket_class = ( bucket.Name == data['selected'] ) ? 'selected' : '';
116
+ $bucketList.append('<li><a class="' + bucket_class + '" href="#" data-bucket="'+ bucket.Name +'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+ bucket.Name +'</span><span class="spinner"></span></span></a></li>');
117
+ });
118
+ } else {
119
+ show_bucket_error( as3cf_i18n.get_buckets_error, data[ 'error' ] );
120
+ }
121
+ }
122
  });
123
+ }
124
+
125
+ $bucketList.on('click', 'a', function(e){
126
+ e.preventDefault();
127
+
128
+ if ( $(this).hasClass('selected') ) {
129
+ $('.as3cf-settings').addClass('as3cf-has-bucket');
130
+ return;
131
+ }
132
+
133
+ var bucket = this;
134
+ $('.as3cf-bucket-list a' ).removeClass('selected');
135
+ $(bucket).addClass('selected');
136
+
137
+ $bucketList.addClass('saving');
138
+ $(bucket).find('.spinner').show();
139
+ var bucketName = $(bucket).attr('data-bucket');
140
+
141
+ var data = {
142
+ action: 'as3cf-save-bucket',
143
+ bucket_name: bucketName,
144
+ _nonce: as3cf_i18n.save_bucket_nonce
145
+ };
146
+
147
+ $.ajax({
148
+ url: ajaxurl,
149
+ type: 'POST',
150
+ dataType: 'JSON',
151
+ data: data,
152
+ error: function(jqXHR, textStatus, errorThrown) {
153
+ $bucketList.removeClass('saving');
154
+ show_bucket_error( as3cf_i18n.save_bucket_error, errorThrown );
155
+ },
156
+ success: function(data, textStatus, jqXHR) {
157
+ $(bucket).find('.spinner').hide();
158
+ $bucketList.removeClass('saving');
159
+ if (typeof data['success'] !== 'undefined') {
160
+ bucket_select( bucketName, data['region'], data['can_write'] );
161
+ } else {
162
+ show_bucket_error( as3cf_i18n.save_bucket_error, data['error'] );
163
+ }
164
+ }
165
+ });
166
+ });
167
+
168
+ function show_bucket_error( title, error ) {
169
+ $( '.as3cf-bucket-error span.title' ).html( title );
170
+ $( '.as3cf-bucket-error span.message' ).html( error );
171
+ $( '.as3cf-bucket-error' ).show();
172
+ }
173
+
174
+ function bucket_select( bucket, region, can_write ) {
175
+ if ( '' == $( '.as3cf-active-bucket' ).text() ) {
176
+ // first time bucket select - enable main options by default
177
+ set_checkbox( 'copy-to-s3-wrap' );
178
+ set_checkbox( 'serve-from-s3-wrap' );
179
+ }
180
+ $( '.as3cf-active-bucket' ).text( bucket );
181
+ $( '#as3cf-bucket' ).val( bucket );
182
+ $( '#as3cf-region' ).val( region );
183
+ $( '.updated' ).show();
184
+ // check permission on bucket
185
+ if( can_write === false){
186
+ $('.as3cf-can-write-error').show();
187
+ }
188
+ $( '.as3cf-settings' ).addClass( 'as3cf-has-bucket' );
189
+ generate_url_preview();
190
+ }
191
 
192
+ $('.as3cf-switch').on('click', 'span', function(e){
193
+ if ( ! $(this).parent().hasClass('disabled') ) {
194
+ var parent_id = $(this).parent().attr('id');
195
+ set_checkbox( parent_id );
196
+ }
197
  });
198
 
199
+ function set_checkbox( checkbox_wrap ) {
200
+ $('#' + checkbox_wrap + ' span' ).toggleClass('checked');
201
+ var switch_on = $('#' + checkbox_wrap + ' span.on' ).hasClass('checked');
202
+ var checkbox_name = $('#' + checkbox_wrap).data('checkbox');
203
+ var $checkbox = $('input#' + checkbox_name);
204
+ $checkbox.attr( "checked", switch_on );
205
+ $checkbox.trigger("change");
206
+ }
207
+
208
+ if ( $( '.as3cf-settings' ).length && ! $( '.as3cf-settings' ).hasClass( 'as3cf-has-bucket' ) ) {
209
+ loadBuckets();
210
+ }
211
+
212
+ if ( $( '#copy-to-s3' ).is( ":checked" ) ) {
213
+ $('tr.advanced-options').show();
214
+ }
215
+
216
+ $('.as3cf-settings').on('change', '#copy-to-s3', function(e){
217
+ $('tr.advanced-options').toggle();
218
+ });
219
+
220
+ if ( $( '#serve-from-s3' ).is( ":checked" ) ) {
221
+ $('tr.configure-url').show();
222
+ }
223
+
224
+ $('.as3cf-settings').on('change', '#serve-from-s3', function(e){
225
+ $('tr.configure-url').toggle();
226
+ });
227
+
228
+ $('.as3cf-settings').on('change', '.sub-toggle', function(e){
229
+ var setting = $(this ).attr('id');
230
+ $('.as3cf-setting.' + setting ).toggleClass('hide');
231
+ });
232
+
233
+ $('.as3cf-domain').on('change', 'input[type="radio"]', function(e){
234
+ var domain = $( 'input:radio[name="domain"]:checked' ).val();
235
+ if ( 'cloudfront' == domain && $('.as3cf-setting.cloudfront' ).hasClass('hide') ) {
236
+ $('.as3cf-setting.cloudfront' ).removeClass('hide');
237
+ } else {
238
+ $('.as3cf-setting.cloudfront' ).addClass('hide');
239
+ }
240
+ });
241
+
242
+ $( '.as3cf-ssl' ).on( 'change', 'input[type="radio"]', function( e ) {
243
+ var ssl = $( 'input:radio[name="ssl"]:checked' ).val();
244
+ if ( 'https' == ssl ) {
245
+ var domain = $( 'input:radio[name="domain"]:checked' ).val();
246
+ if ( 'subdomain' == domain ) {
247
+ $( 'input[name="domain"][value="path"]' ).attr( "checked", true );
248
+ }
249
+ $( '.subdomain-wrap input' ).attr( 'disabled', true );
250
+ $( '.subdomain-wrap' ).addClass( 'disabled' );
251
+ } else {
252
+ $( '.subdomain-wrap input' ).removeAttr( 'disabled' );
253
+ $( '.subdomain-wrap' ).removeClass( 'disabled' );
254
+ }
255
+ } );
256
+
257
+ $('.url-preview').on('change', 'input', function(e){
258
+ generate_url_preview();
259
+ });
260
+
261
+ function generate_url_preview() {
262
+ $('.as3cf-url-preview' ).html( 'Generating...' );
263
+
264
+ var data = {
265
+ _nonce: as3cf_i18n.get_url_preview_nonce
266
+ };
267
+
268
+ $.each( $(".as3cf-main-settings form").serializeArray(), function(i,o){
269
+ var n = o.name,
270
+ v = o.value;
271
+ n = n.replace('[]', '');
272
+ data[n] = data[n] === undefined ? v
273
+ : $.isArray( data[n] ) ? data[n].concat( v )
274
+ : [ data[n], v ];
275
+ });
276
+
277
+ // overwrite the save action stored in the form
278
+ data['action'] = 'as3cf-get-url-preview';
279
+
280
+ $.ajax({
281
+ url: ajaxurl,
282
+ type: 'POST',
283
+ dataType: 'JSON',
284
+ data: data,
285
+ error: function(jqXHR, textStatus, errorThrown) {
286
+ alert(as3cf_i18n.get_url_preview_error + errorThrown);
287
+ },
288
+ success: function(data, textStatus, jqXHR) {
289
+ if (typeof data['success'] !== 'undefined') {
290
+ $('.as3cf-url-preview' ).html( data['url'] );
291
+ } else {
292
+ alert(as3cf_i18n.get_url_preview_error + data['error']);
293
+ }
294
+ }
295
+ });
296
+ }
297
+
298
+ // save the original state of the form for comparison later
299
+ saved_settings = $( '.as3cf-main-settings form' ).serialize();
300
+
301
+ // let the save settings submit happen as normal
302
+ $( document ).on( 'submit', '.as3cf-main-settings form', function( event ) {
303
+ // disable unload warning
304
+ $( window ).off( 'beforeunload.as3cf-settings' );
305
+ } );
306
+
307
+ // prompt user with dialog if leaving the settings page with unsaved changes
308
+ $( window ).on( 'beforeunload.as3cf-settings', function() {
309
+ if ( $( '.as3cf-main-settings form' ).serialize() != saved_settings ) {
310
+ return as3cf_i18n.save_alert;
311
+ }
312
+ } );
313
+
314
  });
315
 
316
  })(jQuery);
assets/js/script.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){a(document).ready(function(){a(".as3cf-settings").each(function(){var b=a(this);a("select.bucket",b).change(function(){var b=a(this);if("new"===b.val()){var c=function(a,c,d){alert(as3cf_i18n.create_bucket_error+d),b[0].selectedIndex=0,console.log(a),console.log(c),console.log(d)},d=function(c){if("undefined"!=typeof c.success){var d=document.createElement("option");d.value=d.innerHTML=e;var f=0;a("option",b).each(function(){return a(this).val()==e?!1:a(this).val()>e||"new"==a(this).val()?(a(d).insertBefore(a(this)),!1):void(f+=1)}),b[0].selectedIndex=f,as3cf_i18n.create_bucket_nonce=c._nonce}else alert(as3cf_i18n.create_bucket_error+c.error),b[0].selectedIndex=0},e=window.prompt(as3cf_i18n.create_bucket_prompt);if(!e)return void(b[0].selectedIndex=0);var f={action:"as3cf-create-bucket",bucket_name:e,_nonce:as3cf_i18n.create_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",success:d,error:c,data:f})}})})})}(jQuery);
1
+ !function(a){var b;a(document).ready(function(){function c(){a(".as3cf-bucket-error").hide(),h.html('<li class="loading">'+h.attr("data-working")+"</li>");var b={action:"as3cf-get-buckets",_nonce:as3cf_i18n.get_buckets_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){h.html(""),d(as3cf_i18n.get_buckets_error,c)},success:function(b){h.html(""),"undefined"!=typeof b.success?a(b.buckets).each(function(a,c){var d=c.Name==b.selected?"selected":"";h.append('<li><a class="'+d+'" href="#" data-bucket="'+c.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+c.Name+'</span><span class="spinner"></span></span></a></li>')}):d(as3cf_i18n.get_buckets_error,b.error)}})}function d(b,c){a(".as3cf-bucket-error span.title").html(b),a(".as3cf-bucket-error span.message").html(c),a(".as3cf-bucket-error").show()}function e(b,c,d){""==a(".as3cf-active-bucket").text()&&(f("copy-to-s3-wrap"),f("serve-from-s3-wrap")),a(".as3cf-active-bucket").text(b),a("#as3cf-bucket").val(b),a("#as3cf-region").val(c),a(".updated").show(),d===!1&&a(".as3cf-can-write-error").show(),a(".as3cf-settings").addClass("as3cf-has-bucket"),g()}function f(b){a("#"+b+" span").toggleClass("checked");var c=a("#"+b+" span.on").hasClass("checked"),d=a("#"+b).data("checkbox"),e=a("input#"+d);e.attr("checked",c),e.trigger("change")}function g(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf_i18n.get_url_preview_nonce};a.each(a(".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_i18n.get_url_preview_error+c)},success:function(b){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf_i18n.get_url_preview_error+b.error)}})}a(".as3cf-settings").each(function(){var b=a(this),f=a(".as3cf-bucket-list"),g=b.find(".as3cf-create-bucket-form");if(g.length){var h=g.find("button"),i=h.text();g.on("submit",function(b){b.preventDefault(),a(".as3cf-bucket-error").hide(),f.addClass("saving"),h.text(h.attr("data-working")),h.prop("disabled",!0);var j=g.find('input[name="bucket_name"]').val(),k={action:"as3cf-create-bucket",bucket_name:j,_nonce:as3cf_i18n.create_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:k,error:function(a,b,c){h.text(i),d(as3cf_i18n.create_bucket_error,c)},success:function(b){h.text(i),h.prop("disabled",!1),"undefined"!=typeof b.success?(e(j,b.region,b.can_write),g.find('input[name="bucket_name"]').val(""),a(".as3cf-bucket-list a").removeClass("selected"),c(),f.removeClass("saving")):d(as3cf_i18n.create_bucket_error,b.error)}})})}var j=b.find(".as3cf-change-bucket");j.length&&j.on("click",function(b){b.preventDefault(),a(".updated").hide(),a(".as3cf-can-write-error").hide(),a(".as3cf-settings").removeClass("as3cf-has-bucket"),c(),a(".as3cf-active-bucket").html&&a(".as3cf-cancel-bucket-select-wrap").show(),a(".as3cf-bucket-list a.selected").length&&a(".as3cf-bucket-list").scrollTop(a(".as3cf-bucket-list a.selected").position().top-50)});var k=b.find(".as3cf-refresh-buckets");k.length&&k.on("click",function(a){a.preventDefault(),c()});var l=b.find(".as3cf-cancel-bucket-select");l.length&&l.on("click",function(b){b.preventDefault(),a(".as3cf-bucket-error").hide(),a(".as3cf-settings").addClass("as3cf-has-bucket")})});var h=a(".as3cf-bucket-list");h.on("click","a",function(b){if(b.preventDefault(),a(this).hasClass("selected"))return void a(".as3cf-settings").addClass("as3cf-has-bucket");var c=this;a(".as3cf-bucket-list a").removeClass("selected"),a(c).addClass("selected"),h.addClass("saving"),a(c).find(".spinner").show();var f=a(c).attr("data-bucket"),g={action:"as3cf-save-bucket",bucket_name:f,_nonce:as3cf_i18n.save_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(a,b,c){h.removeClass("saving"),d(as3cf_i18n.save_bucket_error,c)},success:function(b){a(c).find(".spinner").hide(),h.removeClass("saving"),"undefined"!=typeof b.success?e(f,b.region,b.can_write):d(as3cf_i18n.save_bucket_error,b.error)}})}),a(".as3cf-switch").on("click","span",function(){if(!a(this).parent().hasClass("disabled")){var b=a(this).parent().attr("id");f(b)}}),a(".as3cf-settings").length&&!a(".as3cf-settings").hasClass("as3cf-has-bucket")&&c(),a("#copy-to-s3").is(":checked")&&a("tr.advanced-options").show(),a(".as3cf-settings").on("change","#copy-to-s3",function(){a("tr.advanced-options").toggle()}),a("#serve-from-s3").is(":checked")&&a("tr.configure-url").show(),a(".as3cf-settings").on("change","#serve-from-s3",function(){a("tr.configure-url").toggle()}),a(".as3cf-settings").on("change",".sub-toggle",function(){var b=a(this).attr("id");a(".as3cf-setting."+b).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(){var b=a('input:radio[name="domain"]:checked').val();"cloudfront"==b&&a(".as3cf-setting.cloudfront").hasClass("hide")?a(".as3cf-setting.cloudfront").removeClass("hide"):a(".as3cf-setting.cloudfront").addClass("hide")}),a(".as3cf-ssl").on("change",'input[type="radio"]',function(){var b=a('input:radio[name="ssl"]:checked').val();if("https"==b){var c=a('input:radio[name="domain"]:checked').val();"subdomain"==c&&a('input[name="domain"][value="path"]').attr("checked",!0),a(".subdomain-wrap input").attr("disabled",!0),a(".subdomain-wrap").addClass("disabled")}else a(".subdomain-wrap input").removeAttr("disabled"),a(".subdomain-wrap").removeClass("disabled")}),a(".url-preview").on("change","input",function(){g()}),b=a(".as3cf-main-settings form").serialize(),a(document).on("submit",".as3cf-main-settings form",function(){a(window).off("beforeunload.as3cf-settings")}),a(window).on("beforeunload.as3cf-settings",function(){return a(".as3cf-main-settings form").serialize()!=b?as3cf_i18n.save_alert:void 0})})}(jQuery);
assets/sass/styles.scss CHANGED
@@ -1,6 +1,5 @@
1
  .aws-main {
2
  .error {
3
- max-width: 835px;
4
  pre {
5
  background: #eaeaea;
6
  background: rgba(0, 0, 0, 0.07);
@@ -8,251 +7,465 @@
8
  padding: 10px 15px;
9
 
10
  code {
11
- padding: 0;
12
  background: none;
13
  }
14
  }
15
  }
16
 
17
- .as3cf-notice {
18
- max-width: 835px;
 
 
 
 
 
 
 
19
  }
20
  }
21
 
22
  .as3cf-settings {
23
- position: relative;
24
- width: 550px;
25
- min-height: 800px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- select.bucket {
28
- margin-bottom: 5px;
29
- width: 380px;
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
 
 
 
 
 
 
31
 
32
- .form-table {
33
- td {
34
- padding-left: 0;
35
- padding-right: 0;
36
- }
 
37
 
38
- tr:first-child {
39
- h3 {
40
- margin-top: 0;
41
- }
42
- }
 
 
 
43
  }
44
 
45
- .tooltip {
46
- position: relative;
47
- z-index: 2;
48
- cursor: pointer;
 
 
 
 
49
  }
 
50
 
51
- /* Hide the tooltip content by default */
52
- .tooltip:before,
53
- .tooltip:after {
54
- visibility: hidden;
55
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
56
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  opacity: 0;
58
- pointer-events: none;
59
- }
60
-
61
- /* Position tooltip above the element */
62
- .tooltip:before {
63
- position: absolute;
64
- bottom: 150%;
65
- left: 50%;
66
- margin-bottom: 5px;
67
- margin-left: -250px;
68
- padding: 10px;
69
- width: 500px;
70
- -webkit-border-radius: 3px;
71
- -moz-border-radius: 3px;
72
- border-radius: 3px;
73
- background-color: #000;
74
- background-color: hsla(0, 0%, 20%, 0.9);
75
- color: #fff;
76
- content: attr(data-tooltip);
77
- text-align: center;
78
- font-size: 14px;
79
- line-height: 1.3;
80
- }
81
-
82
- /* Triangle hack to make tooltip look like a speech bubble */
83
- .tooltip:after {
84
- position: absolute;
85
- bottom: 150%;
86
- left: 50%;
87
- margin-left: -5px;
88
- width: 0;
89
- border-top: 5px solid #000;
90
- border-top: 5px solid hsla(0, 0%, 20%, 0.9);
91
- border-right: 5px solid transparent;
92
- border-left: 5px solid transparent;
93
- content: " ";
94
- font-size: 0;
95
- line-height: 0;
96
- }
97
-
98
- /* Show tooltip content on hover */
99
- .tooltip:hover:before,
100
- .tooltip:hover:after {
101
- visibility: visible;
102
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
103
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
104
- opacity: 1;
105
  }
106
- }
107
 
108
- .as3cf-banner {
109
- img {
110
- display: block;
111
  }
112
- }
113
 
114
- .as3cf-sidebar {
115
  position: absolute;
116
- top: 17px;
117
- right: -312px;
118
- width: 292px;
119
-
120
- .block {
121
- padding: 20px;
122
- border: 1px solid #ccc;
123
- }
124
-
125
- .subscribe {
126
- border-top: none;
127
-
128
- h2 {
129
- padding: 0;
130
- margin: 0;
131
- margin-bottom: 0.5em;
132
- color: #666;
133
- font-size: 20px;
134
- line-height: 1.2em;
135
- float: none;
136
- }
137
 
138
- h3 {
139
- font-size: 16px;
140
- margin: 0;
141
- }
 
 
 
142
 
143
- p {
144
- margin: 0;
145
- }
 
146
 
147
- .intro {
148
- margin-bottom: 1em;
149
- line-height: 1.4;
150
- }
151
 
152
- ul {
153
- margin-left: 20px;
154
- list-style-type: disc;
 
 
 
 
 
 
155
  }
156
-
157
- li {
158
- line-height: 1.4;
159
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- .links {
162
- margin-bottom: 2em;
 
 
 
163
 
164
- a {
165
- text-decoration: none;
166
- }
167
- }
168
 
169
- .promise {
170
- color: #999;
171
- font-size: 12px;
172
- line-height: 1.4em;
173
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- .field {
176
- margin-bottom: 0.5em;
 
 
177
 
178
- p {
179
- margin-bottom: 0.3em;
180
- }
 
 
181
 
182
- &.submit-button {
183
- margin-bottom: 1em;
184
- }
185
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
 
188
- .credits {
189
- border-top: 0;
 
 
190
 
191
- h4 {
192
- font-size: 16px;
193
- margin-top: 0;
194
- margin-bottom: 10px;
195
- }
196
 
197
- ul {
198
- margin: 0;
199
- }
 
200
 
201
- li {
202
- overflow: hidden;
203
- }
 
204
 
205
- li:last-child {
206
- margin-bottom: 0;
207
- }
208
 
209
- img {
210
- float: left;
211
- margin-right: 10px;
212
- }
213
 
214
- span {
215
- float: left;
216
- display: block;
217
- line-height: 32px;
218
- }
219
 
220
- a {
221
- display: block;
222
- text-decoration: none;
223
- color: #444;
224
- font-size: 16px;
225
- text-align: center;
226
 
227
- &:hover {
228
- color: #888;
229
- }
230
- }
 
 
 
 
 
 
231
  }
 
232
 
233
- @media (min--moz-device-pixel-ratio: 1.3),
234
- (-o-min-device-pixel-ratio: 2.6/2),
235
- (-webkit-min-device-pixel-ratio: 1.3),
236
- (min-device-pixel-ratio: 1.3),
237
- (min-resolution: 1.3dppx) {
238
 
239
- .as3cf-banner {
240
- background-image: url(../img/snail@2x.jpg);
241
- background-size: 292px 165px;
242
- width: 292px;
243
- height: 165px;
244
- display: block;
245
 
246
- img {
247
- display: none;
248
- }
249
- }
 
 
 
 
 
 
 
 
 
 
 
250
  }
251
 
252
- @media screen and (max-width: 1052px) {
253
- position: relative;
254
- top: auto;
255
- right: auto;
256
- margin-top: 50px;
257
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
1
  .aws-main {
2
  .error {
 
3
  pre {
4
  background: #eaeaea;
5
  background: rgba(0, 0, 0, 0.07);
7
  padding: 10px 15px;
8
 
9
  code {
10
+ padding: 0;
11
  background: none;
12
  }
13
  }
14
  }
15
 
16
+ .as3cf-notice, .error {
17
+ max-width: 935px;
18
+ }
19
+
20
+ .updated {
21
+ display: none;
22
+ &.show {
23
+ display: block;
24
+ }
25
  }
26
  }
27
 
28
  .as3cf-settings {
29
+ position: relative;
30
+ width: 650px;
31
+ min-height: 800px;
32
+
33
+ .as3cf-main-settings {
34
+ display: none;
35
+ p {
36
+ font-size: 13px;
37
+ a {
38
+ color: #444;
39
+ }
40
+ }
41
+ }
42
+ &.as3cf-has-bucket {
43
+ .as3cf-bucket-select {
44
+ display: none;
45
+ }
46
+ .as3cf-main-settings {
47
+ display: block;
48
+ }
49
+ }
50
+
51
+ tr.configure-url, tr.advanced-options {
52
+ display: none;
53
+ }
54
 
55
+ .object-prefix-desc {
56
+ em {
57
+ white-space: nowrap;
58
+ }
59
+ }
60
+
61
+ .as3cf-url-preview-wrap {
62
+ background: #ffffff;
63
+ text-align: center;
64
+ padding: 20px 20px 10px;
65
+ max-width: 610px;
66
+ width: 100%;
67
+ .as3cf-url-preview {
68
+ margin-top: 5px;
69
+ overflow-x: scroll;
70
+ padding-bottom: 15px;
71
  }
72
+ span {
73
+ color: #aaa;
74
+ text-transform: uppercase;
75
+ font-weight: bold;
76
+ }
77
+ }
78
 
79
+ .as3cf-ssl {
80
+ p.info {
81
+ margin-top: 10px;
82
+ padding: 0;
83
+ }
84
+ }
85
 
86
+ .as3cf-radio-group {
87
+ label {
88
+ display: block;
89
+ margin-bottom: 10px;
90
+ &.disabled, &.disabled p {
91
+ color: #bbbbbb;
92
+ cursor: default;
93
+ }
94
  }
95
 
96
+ p {
97
+ padding-left: 25px;
98
+ color: #6b6b6b;
99
+ margin: 0;
100
+ font-size: 12px;
101
+ &.as3cf-setting {
102
+ margin-top: 5px;
103
+ }
104
  }
105
+ }
106
 
107
+ .as3cf-switch {
108
+ position: relative;
109
+
110
+ &.disabled {
111
+ span {
112
+ cursor: default;
113
+ margin: 1px 0 0 0;
114
+ }
115
+ span.checked {
116
+ background: #F1F1F1;
117
+ border: 1px solid #dddddd;
118
+ color: #a3a3a3;
119
+ }
120
+ }
121
+ span {
122
+ -webkit-box-sizing: border-box;
123
+ -moz-box-sizing: border-box;
124
+ box-sizing: border-box;
125
+ cursor: pointer;
126
+ float: left;
127
+ display: inline-block;
128
+ height: 100%;
129
+ font-size: 12px;
130
+ line-height: 20px;
131
+ border-radius: 2px;
132
+ -webkit-border-radius: 2px;
133
+ font-weight: bold;
134
+ padding: 4px 8px;
135
+ background: #F1F1F1;
136
+ border: 1px solid #dddddd;
137
+ color: #CCCCCC;
138
+ margin: 1px -1px 0;
139
+ z-index: 1;
140
+
141
+ &.checked {
142
+ background: #ffffff;
143
+ border-color: #E4E4E4;
144
+ color: #373737;
145
+ padding: 5px 10px;
146
+ margin: 0;
147
+ z-index: 2;
148
+ }
149
+ }
150
+ input[type="checkbox"] {
151
+ position: absolute !important;
152
+ top: 0;
153
+ left: 0;
154
  opacity: 0;
155
+ filter: alpha(opacity=0);
156
+ z-index: -1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
+ }
159
 
160
+ .as3cf-setting {
161
+ &.hide{
162
+ display: none;
163
  }
164
+ }
165
 
166
+ .as3cf-bucket-actions {
167
  position: absolute;
168
+ right: 0;
169
+ top: 2px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ .as3cf-cancel-bucket-select-wrap {
172
+ display: none;
173
+ &:after {
174
+ content: " | "
175
+ }
176
+ }
177
+ }
178
 
179
+ .as3cf-bucket-list-wrapper {
180
+ background: #fff;
181
+ padding: 15px 20px;
182
+ margin-bottom: 2em;
183
 
184
+ .as3cf-bucket-list {
185
+ max-height: 300px;
186
+ overflow: auto;
187
+ margin: 0;
188
 
189
+ li {
190
+ font-size: 14px;
191
+ padding: 10px 0;
192
+ }
193
+ a {
194
+ color: #444;
195
+ text-decoration: none;
196
+ &:hover, &.selected {
197
+ color: #0074A2;
198
  }
199
+ &.selected {
200
+ font-weight: bold;
 
201
  }
202
+ .bucket {
203
+ float: left;
204
+ clear: both;
205
+ .dashicons {
206
+ margin-right: 5px;
207
+ }
208
+ }
209
+ .spinner {
210
+ float: left;
211
+ background-size: 15px 15px
212
+ }
213
+ }
214
+ &.saving {
215
+ opacity: 0.5;
216
+ }
217
+ }
218
+ }
219
 
220
+ .as3cf-create-bucket-form {
221
+ input[type="text"] {
222
+ width: 80%;
223
+ }
224
+ }
225
 
226
+ select.bucket {
227
+ margin-bottom: 5px;
228
+ width: 380px;
229
+ }
230
 
231
+ .form-table {
232
+ td {
233
+ padding-left: 0;
234
+ padding-right: 0;
235
+ &:first-child {
236
+ vertical-align: top;
237
+ min-width: 120px;
238
+ }
239
+ }
240
+ h3 {
241
+ font-weight: normal;
242
+ text-transform: uppercase;
243
+ margin: 0;
244
+ }
245
+ h4 {
246
+ margin: 0;
247
+ }
248
+ .as3cf-border-bottom td {
249
+ border-bottom: 1px solid #ddd;
250
+ }
251
+ }
252
 
253
+ .as3cf-active-bucket {
254
+ font-weight: bold;
255
+ margin-right: 10px;
256
+ }
257
 
258
+ .tooltip {
259
+ position: relative;
260
+ z-index: 2;
261
+ cursor: pointer;
262
+ }
263
 
264
+ /* Hide the tooltip content by default */
265
+ .tooltip:before,
266
+ .tooltip:after {
267
+ visibility: hidden;
268
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
269
+ filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
270
+ opacity: 0;
271
+ pointer-events: none;
272
+ }
273
+
274
+ /* Position tooltip above the element */
275
+ .tooltip:before {
276
+ position: absolute;
277
+ bottom: 150%;
278
+ left: 50%;
279
+ margin-bottom: 5px;
280
+ margin-left: -250px;
281
+ padding: 10px;
282
+ width: 500px;
283
+ -webkit-border-radius: 3px;
284
+ -moz-border-radius: 3px;
285
+ border-radius: 3px;
286
+ background-color: #000;
287
+ background-color: hsla(0, 0%, 20%, 0.9);
288
+ color: #fff;
289
+ content: attr(data-tooltip);
290
+ text-align: center;
291
+ font-size: 14px;
292
+ line-height: 1.3;
293
+ }
294
+
295
+ /* Triangle hack to make tooltip look like a speech bubble */
296
+ .tooltip:after {
297
+ position: absolute;
298
+ bottom: 150%;
299
+ left: 50%;
300
+ margin-left: -5px;
301
+ width: 0;
302
+ border-top: 5px solid #000;
303
+ border-top: 5px solid hsla(0, 0%, 20%, 0.9);
304
+ border-right: 5px solid transparent;
305
+ border-left: 5px solid transparent;
306
+ content: " ";
307
+ font-size: 0;
308
+ line-height: 0;
309
+ }
310
+
311
+ /* Show tooltip content on hover */
312
+ .tooltip:hover:before,
313
+ .tooltip:hover:after {
314
+ visibility: visible;
315
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
316
+ filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
317
+ opacity: 1;
318
+ }
319
+ }
320
+
321
+ .as3cf-banner {
322
+ img {
323
+ display: block;
324
+ }
325
+ }
326
+
327
+ .as3cf-sidebar {
328
+ position: absolute;
329
+ top: 17px;
330
+ right: -312px;
331
+ width: 292px;
332
+
333
+ .block {
334
+ padding: 20px;
335
+ border: 1px solid #ccc;
336
+ }
337
+
338
+ .subscribe {
339
+ border-top: none;
340
+
341
+ h2 {
342
+ padding: 0;
343
+ margin: 0;
344
+ margin-bottom: 0.5em;
345
+ color: #666;
346
+ font-size: 20px;
347
+ line-height: 1.2em;
348
+ float: none;
349
  }
350
 
351
+ h3 {
352
+ font-size: 16px;
353
+ margin: 0;
354
+ }
355
 
356
+ p {
357
+ margin: 0;
358
+ }
 
 
359
 
360
+ .intro {
361
+ margin-bottom: 1em;
362
+ line-height: 1.4;
363
+ }
364
 
365
+ ul {
366
+ margin-left: 20px;
367
+ list-style-type: disc;
368
+ }
369
 
370
+ li {
371
+ line-height: 1.4;
372
+ }
373
 
374
+ .links {
375
+ margin-bottom: 2em;
 
 
376
 
377
+ a {
378
+ text-decoration: none;
379
+ }
380
+ }
 
381
 
382
+ .promise {
383
+ color: #999;
384
+ font-size: 12px;
385
+ line-height: 1.4em;
386
+ }
 
387
 
388
+ .field {
389
+ margin-bottom: 0.5em;
390
+
391
+ p {
392
+ margin-bottom: 0.3em;
393
+ }
394
+
395
+ &.submit-button {
396
+ margin-bottom: 1em;
397
+ }
398
  }
399
+ }
400
 
401
+ .credits {
402
+ border-top: 0;
 
 
 
403
 
404
+ h4 {
405
+ font-size: 16px;
406
+ margin-top: 0;
407
+ margin-bottom: 10px;
408
+ }
 
409
 
410
+ ul {
411
+ margin: 0;
412
+ }
413
+
414
+ li {
415
+ overflow: hidden;
416
+ }
417
+
418
+ li:last-child {
419
+ margin-bottom: 0;
420
+ }
421
+
422
+ img {
423
+ float: left;
424
+ margin-right: 10px;
425
  }
426
 
427
+ span {
428
+ float: left;
429
+ display: block;
430
+ line-height: 32px;
 
431
  }
432
+
433
+ a {
434
+ display: block;
435
+ text-decoration: none;
436
+ color: #444;
437
+ font-size: 16px;
438
+ text-align: center;
439
+
440
+ &:hover {
441
+ color: #888;
442
+ }
443
+ }
444
+ }
445
+
446
+ @media (min--moz-device-pixel-ratio: 1.3),
447
+ (-o-min-device-pixel-ratio: 2.6/2),
448
+ (-webkit-min-device-pixel-ratio: 1.3),
449
+ (min-device-pixel-ratio: 1.3),
450
+ (min-resolution: 1.3dppx) {
451
+
452
+ .as3cf-banner {
453
+ background-image: url(../img/snail@2x.jpg);
454
+ background-size: 292px 165px;
455
+ width: 292px;
456
+ height: 165px;
457
+ display: block;
458
+
459
+ img {
460
+ display: none;
461
+ }
462
+ }
463
+ }
464
+
465
+ @media screen and (max-width: 1052px) {
466
+ position: relative;
467
+ top: auto;
468
+ right: auto;
469
+ margin-top: 50px;
470
+ }
471
  }
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -25,18 +25,45 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
25
  $this->plugin_title = __( 'Amazon S3 and CloudFront', 'as3cf' );
26
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
27
 
 
 
28
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
 
29
 
30
  add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 );
31
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
32
  add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 100, 2 );
33
  add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
34
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
 
 
35
 
36
  load_plugin_textdomain( 'as3cf', false, dirname( plugin_basename( $plugin_file_path ) ) . '/languages/' );
37
  }
38
 
 
 
 
 
 
 
 
 
39
  function get_setting( $key, $default = '' ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  $settings = $this->get_settings();
41
 
42
  // If legacy setting set, migrate settings
@@ -45,22 +72,91 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
45
  }
46
 
47
  // Default object prefix
48
- if ( 'object-prefix' == $key && !isset( $settings['object-prefix'] ) ) {
49
- $uploads = wp_upload_dir();
50
- $parts = parse_url( $uploads['baseurl'] );
51
- return substr( $parts['path'], 1 ) . '/';
52
  }
53
 
54
- if ( 'bucket' == $key && defined( 'AS3CF_BUCKET' ) ) {
55
- $value = AS3CF_BUCKET;
 
56
  }
57
- else {
58
- $value = parent::get_setting( $key, $default );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  return apply_filters( 'as3cf_setting_' . $key, $value );
62
  }
63
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  /**
65
  * Allowed mime types array that can be edited for specific S3 uploading
66
  *
@@ -70,12 +166,49 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
70
  return apply_filters( 'as3cf_allowed_mime_types', get_allowed_mime_types() );
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  /**
74
  * Find backup images and add to array for removal
75
  *
76
- * @param unknown $post_id
77
- * @param unknown $objects
78
- * @param unknown $path
79
  */
80
  function prepare_backup_size_images_to_remove( $post_id, &$objects, $path ) {
81
  $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
@@ -92,9 +225,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
92
  /**
93
  * Find intermediate size images and add to array for removal
94
  *
95
- * @param unknown $post_id
96
- * @param unknown $objects
97
- * @param unknown $path
98
  */
99
  function prepare_intermediate_images_to_remove( $post_id, &$objects, $path ) {
100
  $intermediate_images = get_intermediate_image_sizes();
@@ -111,8 +244,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
111
  /**
112
  * Delete bulk objects from an S3 bucket
113
  *
114
- * @param unknown $bucket
115
- * @param unknown $objects
116
  * @param bool $log_error
117
  * @param bool $return_on_error
118
  */
@@ -120,7 +253,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
120
  try {
121
  $this->get_s3client()->deleteObjects( array(
122
  'Bucket' => $bucket,
123
- 'Objects' => $objects
124
  ) );
125
  } catch ( Exception $e ) {
126
  if ( $log_error ) {
@@ -133,11 +266,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
133
  }
134
 
135
  function delete_attachment( $post_id ) {
136
- if ( !$this->is_plugin_setup() ) {
137
  return;
138
  }
139
 
140
- if ( !( $s3object = $this->get_attachment_s3_info( $post_id ) ) ) {
141
  return;
142
  }
143
 
@@ -146,7 +279,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
146
  $this->set_s3client_region( $s3object );
147
 
148
  $amazon_path = dirname( $s3object['key'] );
149
- $objects = array();
150
 
151
  // remove intermediate images
152
  $this->prepare_intermediate_images_to_remove( $post_id, $objects, $amazon_path );
@@ -167,7 +300,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
167
 
168
  // add main file to be deleted
169
  $objects[] = array(
170
- 'Key' => $s3object['key']
171
  );
172
 
173
  $this->delete_s3_objects( $bucket, $objects, true, true );
@@ -176,7 +309,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
176
  }
177
 
178
  function wp_update_attachment_metadata( $data, $post_id ) {
179
- if ( !$this->get_setting( 'copy-to-s3' ) || !$this->is_plugin_setup() ) {
180
  return $data;
181
  }
182
 
@@ -219,12 +352,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
219
  $time = date( 'Y/m', $time );
220
  }
221
 
222
- $prefix = ltrim( trailingslashit( $this->get_setting( 'object-prefix' ) ), '/' );
223
- $prefix .= ltrim( trailingslashit( $this->get_dynamic_prefix( $time ) ), '/' );
224
 
225
- if ( $this->get_setting( 'object-versioning' ) ) {
226
- $prefix .= $this->get_object_version_string( $post_id );
227
- }
228
  // use bucket from settings
229
  $bucket = $this->get_setting( 'bucket' );
230
  }
@@ -239,7 +368,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
239
 
240
  $s3object = array(
241
  'bucket' => $bucket,
242
- 'key' => $prefix . $file_name
243
  );
244
 
245
  // store acl if not default
@@ -259,12 +388,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
259
  'Bucket' => $bucket,
260
  'Key' => $prefix . $file_name,
261
  'SourceFile' => $file_path,
262
- 'ACL' => $acl
263
  );
264
 
265
  // If far future expiration checked (10 years)
266
  if ( $this->get_setting( 'expires' ) ) {
267
- $args['Expires'] = date( 'D, d M Y H:i:s O', time()+315360000 );
268
  }
269
 
270
  // if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
@@ -304,18 +433,17 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
304
  if ( file_exists( $path ) ) {
305
  $additional_images[] = array(
306
  'Key' => $prefix . $data['thumb'],
307
- 'SourceFile' => $path
308
  );
309
  $files_to_remove[] = $path;
310
  }
311
- }
312
- elseif ( !empty( $data['sizes'] ) ) {
313
  foreach ( $data['sizes'] as $size ) {
314
  $path = str_replace( $file_name, $size['file'], $file_path );
315
  if ( file_exists( $path ) ) {
316
  $additional_images[] = array(
317
  'Key' => $prefix . $size['file'],
318
- 'SourceFile' => $path
319
  );
320
  $files_to_remove[] = $path;
321
  }
@@ -332,7 +460,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
332
  if ( file_exists( $hidpi_path ) ) {
333
  $hidpi_images[] = array(
334
  'Key' => $this->get_hidpi_file_path( $image['Key'] ),
335
- 'SourceFile' => $hidpi_path
336
  );
337
  $files_to_remove[] = $hidpi_path;
338
  }
@@ -361,7 +489,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
361
 
362
  function remove_local_files( $file_paths ) {
363
  foreach ( $file_paths as $path ) {
364
- if ( !@unlink( $path ) ) {
365
  error_log( 'Error removing local file ' . $path );
366
  }
367
  }
@@ -369,15 +497,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
369
 
370
  function get_hidpi_file_path( $orig_path ) {
371
  $hidpi_suffix = apply_filters( 'as3cf_hidpi_suffix', '@2x' );
372
- $pathinfo = pathinfo( $orig_path );
 
373
  return $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $hidpi_suffix . '.' . $pathinfo['extension'];
374
  }
375
 
376
  function get_object_version_string( $post_id ) {
377
- if ( get_option( 'uploads_use_yearmonth_folders' ) ) {
378
  $date_format = 'dHis';
379
- }
380
- else {
381
  $date_format = 'YmdHis';
382
  }
383
 
@@ -394,15 +522,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
394
  function get_attachment_folder_time( $post_id ) {
395
  $time = current_time( 'timestamp' );
396
 
397
- if ( !( $attach = get_post( $post_id ) ) ) {
398
  return $time;
399
  }
400
 
401
- if ( !$attach->post_parent ) {
402
  return $time;
403
  }
404
 
405
- if ( !( $post = get_post( $attach->post_parent ) ) ) {
406
  return $time;
407
  }
408
 
@@ -492,16 +620,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
492
  }
493
 
494
  function is_plugin_setup() {
495
- return (bool) $this->get_setting( 'bucket' ) && !is_wp_error( $this->aws->get_client() );
496
  }
497
 
498
  /**
499
  * Generate a link to download a file from Amazon S3 using query string
500
  * authentication. This link is only valid for a limited amount of time.
501
  *
502
- * @param unknown $post_id Post ID of the attachment
503
  * @param int $expires Seconds for the link to live
504
- * @param null $size Size of the image to get
505
  *
506
  * @return mixed|void|WP_Error
507
  */
@@ -512,13 +640,125 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
512
  return $this->get_attachment_url( $post_id, $expires, $size );
513
  }
514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  /**
516
  * Get the url of the file from Amazon S3
517
  *
518
- * @param unknown $post_id Post ID of the attachment
519
- * @param null $expires Seconds for the link to live
520
- * @param null $size Size of the image to get
521
- * @param null $meta Pre retrieved _wp_attachment_metadata for the attachment
522
  *
523
  * @return bool|mixed|void|WP_Error
524
  */
@@ -532,12 +772,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
532
  return false;
533
  }
534
 
535
- if ( is_ssl() || $this->get_setting( 'force-ssl' ) ) {
536
- $scheme = 'https';
537
- }
538
- else {
539
- $scheme = 'http';
540
- }
541
 
542
  // We don't use $this->get_s3object_region() here because we don't want
543
  // to make an AWS API call and slow down page loading
@@ -553,31 +788,18 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
553
  $expires = self::DEFAULT_EXPIRES;
554
  }
555
 
556
- $prefix = ( '' == $region ) ? 's3' : 's3-' . $region;
557
-
558
- if ( is_null( $expires ) && $this->get_setting( 'cloudfront' ) ) {
559
- $domain_bucket = $this->get_setting( 'cloudfront' );
560
- }
561
- elseif ( $this->get_setting( 'virtual-host' ) ) {
562
- $domain_bucket = $s3object['bucket'];
563
- }
564
- elseif ( is_ssl() || $this->get_setting( 'force-ssl' ) ) {
565
- $domain_bucket = $prefix . '.amazonaws.com/' . $s3object['bucket'];
566
- }
567
- else {
568
- $domain_bucket = $s3object['bucket'] . '.' . $prefix . '.amazonaws.com';
569
- }
570
 
571
  if ( $size ) {
572
  if ( is_null( $meta ) ) {
573
  $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
574
  }
575
- if ( isset( $meta['sizes'][$size]['file'] ) ) {
576
- $s3object['key'] = dirname( $s3object['key'] ) . '/' . $meta['sizes'][$size]['file'];
577
  }
578
  }
579
 
580
- if ( !is_null( $expires ) ) {
581
  try {
582
  $expires = time() + $expires;
583
  $secure_url = $this->get_s3client()->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires );
@@ -613,8 +835,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
613
  /**
614
  * Encodes the file names for resized image files for an attachment where necessary
615
  *
616
- * @param unknown $data
617
- * @param unknown $post_id
618
  *
619
  * @return mixed Attachment meta data
620
  */
@@ -649,7 +871,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
649
  * Encode file names according to RFC 3986 when generating urls
650
  * As per Amazon https://forums.aws.amazon.com/thread.jspa?threadID=55746#jive-message-244233
651
  *
652
- * @param unknown $file
653
  *
654
  * @return string Encoded filename with path prefix untouched
655
  */
@@ -661,29 +883,118 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
661
  return $file_path . $file_name;
662
  }
663
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  function verify_ajax_request() {
665
- if ( !is_admin() || !wp_verify_nonce( $_POST['_nonce'], $_POST['action'] ) ) {
666
  wp_die( __( 'Cheatin&#8217; eh?', 'as3cf' ) );
667
  }
668
 
669
- if ( !current_user_can( 'manage_options' ) ) {
670
  wp_die( __( 'You do not have sufficient permissions to access this page.', 'as3cf' ) );
671
  }
672
  }
673
 
 
 
 
 
 
 
 
 
 
674
  function ajax_create_bucket() {
675
  $this->verify_ajax_request();
676
 
677
- if ( !isset( $_POST['bucket_name'] ) || !$_POST['bucket_name'] ) {
678
- wp_die( __( 'No bucket name provided.', 'as3cf' ) );
679
- }
680
 
681
- $result = $this->create_bucket( $_POST['bucket_name'] );
682
  if ( is_wp_error( $result ) ) {
683
  $out = array( 'error' => $result->get_error_message() );
684
- }
685
- else {
686
- $out = array( 'success' => '1', '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ) );
 
 
 
 
 
 
 
 
 
 
687
  }
688
 
689
  echo json_encode( $out );
@@ -701,6 +1012,53 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
701
  return true;
702
  }
703
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  function admin_menu( $aws ) {
705
  $hook_suffix = $aws->add_page( $this->plugin_title, $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
706
  add_action( 'load-' . $hook_suffix , array( $this, 'plugin_load' ) );
@@ -715,24 +1073,43 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
715
  }
716
 
717
  /**
718
- * Get the region of the bucket.
719
  *
 
720
  *
721
- * @param unknown $s3object
722
- * @param unknown $post_id - if supplied will update the s3 meta if no region found
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
723
  *
724
- * @return string - region name
 
 
 
 
725
  */
726
  function get_s3object_region( $s3object, $post_id = null ) {
727
  if ( ! isset( $s3object['region'] ) ) {
728
  // if region hasn't been stored in the s3 metadata retrieve using the bucket
729
- try {
730
- $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $s3object['bucket'] ) );
731
- }
732
- catch ( Exception $e ) {
733
- return new WP_Error( 'exception', $e->getMessage() );
734
  }
735
- $s3object['region'] = $region['Location'];
 
736
 
737
  if ( ! is_null( $post_id ) ) {
738
  // retrospectively update s3 metadata with region
@@ -768,8 +1145,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
768
  *
769
  * This is needed for non US standard buckets to add and delete files.
770
  *
771
- * @param unknown $s3object
772
- * @param unknown $post_id
773
  *
774
  * @return string - region name
775
  */
@@ -788,6 +1165,32 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
788
  return $region;
789
  }
790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  function get_buckets() {
792
  try {
793
  $result = $this->get_s3client()->listBuckets();
@@ -802,63 +1205,53 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
802
  /**
803
  * Checks the user has write permission for S3
804
  *
 
 
 
805
  * @return bool
806
  */
807
- function check_write_permission( ) {
808
- if ( ! ( $bucket = $this->get_setting('bucket') ) ) {
809
- // if no bucket set then no need check
810
- return true;
811
- }
812
- // fire up the filesystem API
813
- $filesystem = WP_Filesystem();
814
- global $wp_filesystem;
815
- if ( false === $filesystem || is_null( $wp_filesystem ) ) {
816
- return new WP_Error( 'exception', __( 'There was an error attempting to access the local file system whilst checking the bucket permissions', 'as3cf' ) );
817
  }
818
 
819
- $uploads = wp_upload_dir();
820
  $file_name = 'as3cf-permission-check.txt';
821
- $file = trailingslashit( $uploads['basedir'] ) . $file_name;
822
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'as3cf' );
823
- // create a temp file to upload
824
- $temp_file = $wp_filesystem->put_contents( $file, $file_contents, FS_CHMOD_FILE );
825
- if ( false === $temp_file ) {
826
- return new WP_Error( 'exception', __( 'It looks like we cannot create a file locally to test the S3 permissions', 'as3cf' ) );
827
- }
828
 
829
  $path = $this->get_setting( 'object-prefix' );
830
- $key = $path . $file_name;
831
 
832
  $args = array(
833
- 'Bucket' => $bucket,
834
- 'Key' => $key,
835
- 'SourceFile' => $file,
836
- 'ACL' => 'public-read'
837
  );
838
 
839
  try {
840
  // need to set region for buckets in non default region
841
- $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
842
- if ( $region['Location'] ) {
843
- $region = $this->translate_region( $region['Location'] );
 
844
  $this->get_s3client()->setRegion( $region );
845
  }
846
  // attempt to create the test file
847
  $this->get_s3client()->putObject( $args );
848
  // delete it straight away if created
849
  $this->get_s3client()->deleteObject( array(
850
- 'Bucket' => $bucket,
851
- 'Key' => $key
852
- ) );
853
  $can_write = true;
854
  } catch ( Exception $e ) {
855
  // write permission not found
856
  $can_write = false;
857
  }
858
 
859
- // delete temp file
860
- $wp_filesystem->delete( $file );
861
-
862
  return $can_write;
863
  }
864
 
@@ -874,33 +1267,42 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
874
  wp_enqueue_script( 'as3cf-script', $src, array( 'jquery' ), $version, true );
875
 
876
  wp_localize_script( 'as3cf-script', 'as3cf_i18n', array(
877
- 'create_bucket_prompt' => __( 'Bucket Name:', 'as3cf' ),
878
- 'create_bucket_error' => __( 'Error creating bucket: ', 'as3cf' ),
879
- 'create_bucket_nonce' => wp_create_nonce( 'as3cf-create-bucket' )
880
- ) );
 
 
 
 
 
 
 
881
 
882
  $this->handle_post_request();
883
  }
884
 
885
  function handle_post_request() {
886
- if ( empty( $_POST['action'] ) || 'save' != $_POST['action'] ) {
887
  return;
888
  }
889
 
890
- if ( empty( $_POST['_wpnonce'] ) || !wp_verify_nonce( $_POST['_wpnonce'], 'as3cf-save-settings' ) ) {
891
  die( __( "Cheatin' eh?", 'amazon-web-services' ) );
892
  }
893
 
894
- $post_vars = array( 'bucket', 'virtual-host', 'expires', 'permissions', 'cloudfront', 'object-prefix', 'copy-to-s3', 'serve-from-s3', 'remove-local-file', 'force-ssl', 'hidpi-images', 'object-versioning' );
895
 
896
  foreach ( $post_vars as $var ) {
897
  $this->remove_setting( $var );
898
 
899
- if ( !isset( $_POST[$var] ) ) {
900
  continue;
901
  }
902
 
903
- $this->set_setting( $var, $_POST[$var] );
 
 
904
  }
905
 
906
  $this->save_settings();
@@ -925,12 +1327,38 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
925
  $this->aws->render_view( 'footer' );
926
  }
927
 
 
 
 
 
 
 
 
928
  function get_dynamic_prefix( $time = null ) {
929
  $uploads = wp_upload_dir( $time );
930
- return str_replace( $this->get_base_upload_path(), '', $uploads['path'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
931
  }
932
 
933
- // Without the multisite subdirectory
 
 
 
 
 
934
  function get_base_upload_path() {
935
  if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
936
  return ABSPATH . UPLOADS;
@@ -958,7 +1386,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
958
  'limit' => false,
959
  'spam' => 0,
960
  'deleted' => 0,
961
- 'archived' => 0
962
  );
963
  $blogs = wp_get_sites( $args );
964
 
25
  $this->plugin_title = __( 'Amazon S3 and CloudFront', 'as3cf' );
26
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
27
 
28
+ add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
29
+ add_action( 'wp_ajax_as3cf-save-bucket', array( $this, 'ajax_save_bucket' ) );
30
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
31
+ add_action( 'wp_ajax_as3cf-get-url-preview', array( $this, 'ajax_get_url_preview' ) );
32
 
33
  add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 );
34
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
35
  add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 100, 2 );
36
  add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
37
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
38
+ add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
39
+ add_filter( 'as3cf_get_attached_file_copy_back_to_local', array( $this, 'regenerate_thumbnails_get_attached_file' ) );
40
 
41
  load_plugin_textdomain( 'as3cf', false, dirname( plugin_basename( $plugin_file_path ) ) . '/languages/' );
42
  }
43
 
44
+ /**
45
+ * Accessor for a plugin setting with conditions to defaults and upgrades
46
+ *
47
+ * @param string $key
48
+ * @param string $default
49
+ *
50
+ * @return int|mixed|string|void
51
+ */
52
  function get_setting( $key, $default = '' ) {
53
+ // use settings from $_POST when generating URL preview via AJAX
54
+ if ( isset( $_POST['action'] ) && 'as3cf-get-url-preview' == sanitize_key( $_POST['action'] ) ) { // input var okay
55
+ $value = 0;
56
+ if ( isset( $_POST[ $key ] ) ) { // input var okay
57
+ $value = $_POST[ $key ]; // input var okay
58
+ if ( is_array( $value ) ) {
59
+ // checkbox is checked
60
+ $value = 1;
61
+ }
62
+ }
63
+
64
+ return $value;
65
+ }
66
+
67
  $settings = $this->get_settings();
68
 
69
  // If legacy setting set, migrate settings
72
  }
73
 
74
  // Default object prefix
75
+ if ( 'object-prefix' == $key && ! isset( $settings['object-prefix'] ) ) {
76
+ return $this->get_default_object_prefix();
 
 
77
  }
78
 
79
+ // Default use year and month folders
80
+ if ( 'use-yearmonth-folders' == $key && ! isset( $settings['use-yearmonth-folders'] ) ) {
81
+ return get_option( 'uploads_use_yearmonth_folders' );
82
  }
83
+
84
+ // Default enable object prefix - enabled unless path is empty
85
+ if ( 'enable-object-prefix' == $key ) {
86
+ if ( isset( $settings['enable-object-prefix'] ) && '0' == $settings['enable-object-prefix'] ) {
87
+ return 0;
88
+ }
89
+
90
+ if ( isset( $settings['object-prefix'] ) && '' == trim( $settings['object-prefix'] ) ) {
91
+ return 0;
92
+ } else {
93
+ return 1;
94
+ }
95
+ }
96
+
97
+ // Region of bucket if not already retrieved
98
+ if ( 'region' == $key && ! isset( $settings['region'] ) ) {
99
+ $bucket = $this->get_setting( 'bucket' );
100
+ $region = $this->get_bucket_region( $bucket );
101
+
102
+ return $region;
103
+ }
104
+
105
+ // Region of bucket translation
106
+ if ( 'region' == $key && isset( $settings['region'] ) ) {
107
+
108
+ return $this->translate_region( $settings['region'] );
109
+ }
110
+
111
+ // Domain setting since 0.8
112
+ if ( 'domain' == $key && ! isset( $settings['domain'] ) ) {
113
+ if ( $this->get_setting( 'cloudfront' ) ) {
114
+ $domain = 'cloudfront';
115
+ } elseif ( $this->get_setting( 'virtual-host' ) ) {
116
+ $domain = 'virtual-host';
117
+ } elseif ( $this->use_ssl() ) {
118
+ $domain = 'path';
119
+ } else {
120
+ $domain = 'subdomain';
121
+ }
122
+
123
+ return $domain;
124
  }
125
 
126
+ // SSL radio buttons since 0.8
127
+ if ( 'ssl' == $key && ! isset( $settings['ssl'] ) ) {
128
+ if ( $this->get_setting( 'force-ssl', false ) ) {
129
+ $ssl = 'https';
130
+ } else {
131
+ $ssl = 'request';
132
+ }
133
+
134
+ return $ssl;
135
+ }
136
+
137
+ if ( 'bucket' == $key && defined( 'AS3CF_BUCKET' ) ) {
138
+ return AS3CF_BUCKET;
139
+ }
140
+
141
+
142
+ $value = parent::get_setting( $key, $default );
143
+
144
+
145
  return apply_filters( 'as3cf_setting_' . $key, $value );
146
  }
147
 
148
+ /**
149
+ * Return the default object prefix
150
+ *
151
+ * @return string
152
+ */
153
+ function get_default_object_prefix() {
154
+ $uploads = wp_upload_dir();
155
+ $parts = parse_url( $uploads['baseurl'] );
156
+
157
+ return substr( $parts['path'], 1 ) . '/';
158
+ }
159
+
160
  /**
161
  * Allowed mime types array that can be edited for specific S3 uploading
162
  *
166
  return apply_filters( 'as3cf_allowed_mime_types', get_allowed_mime_types() );
167
  }
168
 
169
+ /**
170
+ * Generate a preview of the URL of files uploaded to S3
171
+ *
172
+ * @param string $suffix
173
+ *
174
+ * @return string
175
+ */
176
+ function get_url_preview( $suffix = 'photo.jpg' ) {
177
+ $scheme = $this->get_s3_url_scheme();
178
+ $bucket = $this->get_setting( 'bucket' );
179
+ $path = $this->get_file_prefix();
180
+ $region = $this->get_setting( 'region' );
181
+ $domain = $this->get_s3_url_domain( $bucket, $region );
182
+
183
+ $url = $scheme . '://' . $domain . '/' . $path . $suffix;
184
+
185
+ // replace hyphens with non breaking hyphens for formatting
186
+ $url = str_replace( '-', '&#8209;', $url );
187
+
188
+ return $url;
189
+ }
190
+
191
+ /**
192
+ * AJAX handler for get_url_preview()
193
+ */
194
+ function ajax_get_url_preview() {
195
+ $this->verify_ajax_request();
196
+
197
+ $url = $this->get_url_preview();
198
+
199
+ echo json_encode( array(
200
+ 'success' => '1',
201
+ 'url' => $url,
202
+ ) );
203
+ exit;
204
+ }
205
+
206
  /**
207
  * Find backup images and add to array for removal
208
  *
209
+ * @param int $post_id
210
+ * @param array $objects
211
+ * @param string $path
212
  */
213
  function prepare_backup_size_images_to_remove( $post_id, &$objects, $path ) {
214
  $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
225
  /**
226
  * Find intermediate size images and add to array for removal
227
  *
228
+ * @param int $post_id
229
+ * @param array $objects
230
+ * @param string $path
231
  */
232
  function prepare_intermediate_images_to_remove( $post_id, &$objects, $path ) {
233
  $intermediate_images = get_intermediate_image_sizes();
244
  /**
245
  * Delete bulk objects from an S3 bucket
246
  *
247
+ * @param string $bucket
248
+ * @param array $objects
249
  * @param bool $log_error
250
  * @param bool $return_on_error
251
  */
253
  try {
254
  $this->get_s3client()->deleteObjects( array(
255
  'Bucket' => $bucket,
256
+ 'Objects' => $objects,
257
  ) );
258
  } catch ( Exception $e ) {
259
  if ( $log_error ) {
266
  }
267
 
268
  function delete_attachment( $post_id ) {
269
+ if ( ! $this->is_plugin_setup() ) {
270
  return;
271
  }
272
 
273
+ if ( ! ( $s3object = $this->get_attachment_s3_info( $post_id ) ) ) {
274
  return;
275
  }
276
 
279
  $this->set_s3client_region( $s3object );
280
 
281
  $amazon_path = dirname( $s3object['key'] );
282
+ $objects = array();
283
 
284
  // remove intermediate images
285
  $this->prepare_intermediate_images_to_remove( $post_id, $objects, $amazon_path );
300
 
301
  // add main file to be deleted
302
  $objects[] = array(
303
+ 'Key' => $s3object['key'],
304
  );
305
 
306
  $this->delete_s3_objects( $bucket, $objects, true, true );
309
  }
310
 
311
  function wp_update_attachment_metadata( $data, $post_id ) {
312
+ if ( ! $this->get_setting( 'copy-to-s3' ) || ! $this->is_plugin_setup() ) {
313
  return $data;
314
  }
315
 
352
  $time = date( 'Y/m', $time );
353
  }
354
 
355
+ $prefix = $this->get_file_prefix( $time, $post_id );
 
356
 
 
 
 
357
  // use bucket from settings
358
  $bucket = $this->get_setting( 'bucket' );
359
  }
368
 
369
  $s3object = array(
370
  'bucket' => $bucket,
371
+ 'key' => $prefix . $file_name,
372
  );
373
 
374
  // store acl if not default
388
  'Bucket' => $bucket,
389
  'Key' => $prefix . $file_name,
390
  'SourceFile' => $file_path,
391
+ 'ACL' => $acl,
392
  );
393
 
394
  // If far future expiration checked (10 years)
395
  if ( $this->get_setting( 'expires' ) ) {
396
+ $args['Expires'] = date( 'D, d M Y H:i:s O', time() + 315360000 );
397
  }
398
 
399
  // if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
433
  if ( file_exists( $path ) ) {
434
  $additional_images[] = array(
435
  'Key' => $prefix . $data['thumb'],
436
+ 'SourceFile' => $path,
437
  );
438
  $files_to_remove[] = $path;
439
  }
440
+ } else if ( ! empty( $data['sizes'] ) ) {
 
441
  foreach ( $data['sizes'] as $size ) {
442
  $path = str_replace( $file_name, $size['file'], $file_path );
443
  if ( file_exists( $path ) ) {
444
  $additional_images[] = array(
445
  'Key' => $prefix . $size['file'],
446
+ 'SourceFile' => $path,
447
  );
448
  $files_to_remove[] = $path;
449
  }
460
  if ( file_exists( $hidpi_path ) ) {
461
  $hidpi_images[] = array(
462
  'Key' => $this->get_hidpi_file_path( $image['Key'] ),
463
+ 'SourceFile' => $hidpi_path,
464
  );
465
  $files_to_remove[] = $hidpi_path;
466
  }
489
 
490
  function remove_local_files( $file_paths ) {
491
  foreach ( $file_paths as $path ) {
492
+ if ( ! @unlink( $path ) ) {
493
  error_log( 'Error removing local file ' . $path );
494
  }
495
  }
497
 
498
  function get_hidpi_file_path( $orig_path ) {
499
  $hidpi_suffix = apply_filters( 'as3cf_hidpi_suffix', '@2x' );
500
+ $pathinfo = pathinfo( $orig_path );
501
+
502
  return $pathinfo['dirname'] . '/' . $pathinfo['filename'] . $hidpi_suffix . '.' . $pathinfo['extension'];
503
  }
504
 
505
  function get_object_version_string( $post_id ) {
506
+ if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
507
  $date_format = 'dHis';
508
+ } else {
 
509
  $date_format = 'YmdHis';
510
  }
511
 
522
  function get_attachment_folder_time( $post_id ) {
523
  $time = current_time( 'timestamp' );
524
 
525
+ if ( ! ( $attach = get_post( $post_id ) ) ) {
526
  return $time;
527
  }
528
 
529
+ if ( ! $attach->post_parent ) {
530
  return $time;
531
  }
532
 
533
+ if ( ! ( $post = get_post( $attach->post_parent ) ) ) {
534
  return $time;
535
  }
536
 
620
  }
621
 
622
  function is_plugin_setup() {
623
+ return (bool) $this->get_setting( 'bucket' ) && ! is_wp_error( $this->aws->get_client() );
624
  }
625
 
626
  /**
627
  * Generate a link to download a file from Amazon S3 using query string
628
  * authentication. This link is only valid for a limited amount of time.
629
  *
630
+ * @param int $post_id Post ID of the attachment
631
  * @param int $expires Seconds for the link to live
632
+ * @param string $size Size of the image to get
633
  *
634
  * @return mixed|void|WP_Error
635
  */
640
  return $this->get_attachment_url( $post_id, $expires, $size );
641
  }
642
 
643
+ /**
644
+ * Return the scheme to be used in URLs
645
+ *
646
+ * @return string
647
+ */
648
+ function get_s3_url_scheme() {
649
+ if ( $this->use_ssl() ) {
650
+ $scheme = 'https';
651
+ }
652
+ else {
653
+ $scheme = 'http';
654
+ }
655
+
656
+ return $scheme;
657
+ }
658
+
659
+ /**
660
+ * Determine when to use https in URLS
661
+ *
662
+ * @return bool
663
+ */
664
+ function use_ssl( ) {
665
+ $use_ssl = false;
666
+
667
+ $ssl = $this->get_setting( 'ssl' );
668
+
669
+ if ( 'request' == $ssl && is_ssl() ) {
670
+ $use_ssl = true;
671
+ } else if ( 'https' == $ssl ) {
672
+ $use_ssl = true;
673
+ }
674
+
675
+ return apply_filters( 'as3cf_use_ssl', $use_ssl );
676
+ }
677
+
678
+ /**
679
+ * Get the custom object prefix if enabled
680
+ *
681
+ * @return string
682
+ */
683
+ function get_object_prefix() {
684
+ if ( $this->get_setting( 'enable-object-prefix' ) ) {
685
+ $prefix = trim( $this->get_setting( 'object-prefix' ) );
686
+ } else {
687
+ $prefix = '';
688
+ }
689
+
690
+ return $prefix;
691
+ }
692
+
693
+ /**
694
+ * Get the file prefix
695
+ *
696
+ * @param null $time
697
+ * @param null $post_id
698
+ *
699
+ * @return string
700
+ */
701
+ function get_file_prefix( $time = null, $post_id = null ) {
702
+ $prefix = ltrim( trailingslashit( $this->get_object_prefix() ), '/' );
703
+ $prefix .= ltrim( trailingslashit( $this->get_dynamic_prefix( $time ) ), '/' );
704
+
705
+ if ( $this->get_setting( 'object-versioning' ) ) {
706
+ $prefix .= $this->get_object_version_string( $post_id );
707
+ }
708
+
709
+ return $prefix;
710
+ }
711
+
712
+ /**
713
+ * Get the region specific prefix for S3 URL
714
+ *
715
+ * @param $region
716
+ *
717
+ * @return string
718
+ */
719
+ function get_s3_url_prefix( $region = '' ) {
720
+ $prefix = ( '' == $region ) ? 's3' : 's3-' . $region;
721
+
722
+ return $prefix;
723
+ }
724
+
725
+ /**
726
+ * Get the S3 url for the files
727
+ *
728
+ * @param $bucket
729
+ * @param string $region
730
+ * @param int $expires
731
+ *
732
+ * @return mixed|string|void
733
+ */
734
+ function get_s3_url_domain( $bucket, $region = '', $expires = null ) {
735
+ $domain = $this->get_setting( 'domain' );
736
+
737
+ $prefix = $this->get_s3_url_prefix( $region );
738
+
739
+ if ( 'cloudfront' == $domain && is_null( $expires ) && $this->get_setting( 'cloudfront' ) ) {
740
+ $s3_domain = $this->get_setting( 'cloudfront' );
741
+ }
742
+ elseif ( 'virtual-host' == $domain ) {
743
+ $s3_domain = $bucket;
744
+ }
745
+ elseif ( 'path' == $domain || $this->use_ssl() ) {
746
+ $s3_domain = $prefix . '.amazonaws.com/' . $bucket;
747
+ }
748
+ else {
749
+ $s3_domain = $bucket . '.' . $prefix . '.amazonaws.com';
750
+ }
751
+
752
+ return $s3_domain;
753
+ }
754
+
755
  /**
756
  * Get the url of the file from Amazon S3
757
  *
758
+ * @param int $post_id Post ID of the attachment
759
+ * @param int $expires Seconds for the link to live
760
+ * @param string $size Size of the image to get
761
+ * @param array $meta Pre retrieved _wp_attachment_metadata for the attachment
762
  *
763
  * @return bool|mixed|void|WP_Error
764
  */
772
  return false;
773
  }
774
 
775
+ $scheme = $this->get_s3_url_scheme();
 
 
 
 
 
776
 
777
  // We don't use $this->get_s3object_region() here because we don't want
778
  // to make an AWS API call and slow down page loading
788
  $expires = self::DEFAULT_EXPIRES;
789
  }
790
 
791
+ $domain_bucket = $this->get_s3_url_domain( $s3object['bucket'], $region, $expires );
 
 
 
 
 
 
 
 
 
 
 
 
 
792
 
793
  if ( $size ) {
794
  if ( is_null( $meta ) ) {
795
  $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
796
  }
797
+ if ( isset( $meta['sizes'][ $size ]['file'] ) ) {
798
+ $s3object['key'] = dirname( $s3object['key'] ) . '/' . $meta['sizes'][ $size ]['file'];
799
  }
800
  }
801
 
802
+ if ( ! is_null( $expires ) ) {
803
  try {
804
  $expires = time() + $expires;
805
  $secure_url = $this->get_s3client()->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires );
835
  /**
836
  * Encodes the file names for resized image files for an attachment where necessary
837
  *
838
+ * @param array $data
839
+ * @param int $post_id
840
  *
841
  * @return mixed Attachment meta data
842
  */
871
  * Encode file names according to RFC 3986 when generating urls
872
  * As per Amazon https://forums.aws.amazon.com/thread.jspa?threadID=55746#jive-message-244233
873
  *
874
+ * @param string $file
875
  *
876
  * @return string Encoded filename with path prefix untouched
877
  */
883
  return $file_path . $file_name;
884
  }
885
 
886
+ /**
887
+ * Return the S3 URL when the local file is missing
888
+ * unless we know the calling process is and we are happy
889
+ * to copy the file back to the server to be used
890
+ *
891
+ * @param $file
892
+ * @param $attachment_id
893
+ *
894
+ * @return bool|mixed|void|WP_Error
895
+ */
896
+ function get_attached_file( $file, $attachment_id ) {
897
+ if ( file_exists( $file ) || ! $this->get_setting( 'serve-from-s3' ) ) {
898
+ return $file;
899
+ }
900
+
901
+ if ( ! ( $s3object = $this->get_attachment_s3_info( $attachment_id ) ) ) {
902
+ return $file;
903
+ }
904
+
905
+ $url = $this->get_attachment_url( $attachment_id );
906
+
907
+ // the default behaviour is to return the S3 URL, however we can override this
908
+ // and copy back the file to the local server for certain processes
909
+ // where we know it will get removed again via wp_update_attachment_metadata
910
+ $copy_back_to_local = apply_filters( 'as3cf_get_attached_file_copy_back_to_local', false, $file, $attachment_id );
911
+ if ( false === $copy_back_to_local ) {
912
+ // return S3 URL as a fallback
913
+ return $url;
914
+ }
915
+
916
+ // fire up the filesystem API
917
+ $filesystem = WP_Filesystem();
918
+ global $wp_filesystem;
919
+ if ( false === $filesystem || is_null( $wp_filesystem ) ) {
920
+ error_log( __( 'There was an error attempting to access the file system', 'as3cf' ) );
921
+
922
+ return $url;
923
+ }
924
+
925
+ // download the file from S3
926
+ $temp_file = download_url( $url );
927
+ // copy the temp file to the attachments location
928
+ if ( ! $wp_filesystem->copy( $temp_file, $file ) ) {
929
+ // fallback to url
930
+ $file = $url;
931
+ }
932
+ // clear up temp file
933
+ @unlink( $temp_file );
934
+
935
+ return $file;
936
+ }
937
+
938
+ /**
939
+ * Allow the Regenerate Thumbnails plugin to copy the S3 file back to the local
940
+ * server when the file is missing on the server via get_attached_file
941
+ *
942
+ * @param $copy_back_to_local
943
+ *
944
+ * @return bool
945
+ */
946
+ function regenerate_thumbnails_get_attached_file( $copy_back_to_local ) {
947
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
948
+ return $copy_back_to_local;
949
+ }
950
+
951
+ if ( isset( $_POST['action'] ) && 'regeneratethumbnail' == sanitize_key( $_POST['action'] ) ) { // input var okay
952
+ return true;
953
+ }
954
+
955
+ return $copy_back_to_local;
956
+ }
957
+
958
  function verify_ajax_request() {
959
+ if ( ! is_admin() || ! wp_verify_nonce( sanitize_key( $_POST['_nonce'] ), sanitize_key( $_POST['action'] ) ) ) { // input var okay
960
  wp_die( __( 'Cheatin&#8217; eh?', 'as3cf' ) );
961
  }
962
 
963
+ if ( ! current_user_can( 'manage_options' ) ) {
964
  wp_die( __( 'You do not have sufficient permissions to access this page.', 'as3cf' ) );
965
  }
966
  }
967
 
968
+ function ajax_check_bucket() {
969
+ if ( ! isset( $_POST['bucket_name'] ) || ! ( $bucket = sanitize_text_field( $_POST['bucket_name'] ) ) ) { // input var okay
970
+ echo json_encode( array( 'error' => __( 'No bucket name provided.', 'as3cf' ) ) );
971
+ exit;
972
+ }
973
+
974
+ return $bucket;
975
+ }
976
+
977
  function ajax_create_bucket() {
978
  $this->verify_ajax_request();
979
 
980
+ $bucket = $this->ajax_check_bucket();
 
 
981
 
982
+ $result = $this->create_bucket( $bucket );
983
  if ( is_wp_error( $result ) ) {
984
  $out = array( 'error' => $result->get_error_message() );
985
+ } else {
986
+ $region = $this->save_bucket( $bucket );
987
+
988
+ if ( $region !== false ) {
989
+ $out = array(
990
+ 'success' => '1',
991
+ '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ),
992
+ 'region' => $region,
993
+ );
994
+ $out['can_write'] = $this->check_write_permission( $bucket, $region );
995
+ } else {
996
+ $out = array( 'error' => __( 'Failed to retrieve bucket region.', 'as3cf' ) );
997
+ }
998
  }
999
 
1000
  echo json_encode( $out );
1012
  return true;
1013
  }
1014
 
1015
+ function ajax_save_bucket() {
1016
+ $this->verify_ajax_request();
1017
+
1018
+ $bucket = $this->ajax_check_bucket();
1019
+
1020
+ $region = $this->save_bucket( $bucket );
1021
+
1022
+ if ( $region !== false ) {
1023
+ $out = array(
1024
+ 'success' => '1',
1025
+ 'region' => $region,
1026
+ );
1027
+ $out['can_write'] = $this->check_write_permission( $bucket, $region );
1028
+ } else {
1029
+ $out = array( 'error' => __( 'Failed to retrieve bucket region.', 'as3cf' ) );
1030
+ }
1031
+
1032
+ echo json_encode( $out );
1033
+ exit;
1034
+ }
1035
+
1036
+ /**
1037
+ * Save bucket and bucket's region
1038
+ *
1039
+ * @param $bucket_name
1040
+ *
1041
+ * @return string|bool|WP_Error Region on success
1042
+ */
1043
+ function save_bucket( $bucket_name ) {
1044
+ if ( $bucket_name ) {
1045
+ $this->get_settings();
1046
+ // first time bucket select - enable main options by default
1047
+ if ( ! $this->get_setting( 'bucket' ) ) {
1048
+ $this->set_setting( 'copy-to-s3', '1' );
1049
+ $this->set_setting( 'serve-from-s3', '1' );
1050
+ }
1051
+ $this->set_setting( 'bucket', $bucket_name );
1052
+ $region = $this->get_bucket_region( $bucket_name );
1053
+ $this->set_setting( 'region', $region );
1054
+ $this->save_settings();
1055
+
1056
+ return $region;
1057
+ }
1058
+
1059
+ return false;
1060
+ }
1061
+
1062
  function admin_menu( $aws ) {
1063
  $hook_suffix = $aws->add_page( $this->plugin_title, $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
1064
  add_action( 'load-' . $hook_suffix , array( $this, 'plugin_load' ) );
1073
  }
1074
 
1075
  /**
1076
+ * Get the region of a bucket
1077
  *
1078
+ * @param $bucket
1079
  *
1080
+ * @return string|WP_Error
1081
+ */
1082
+ function get_bucket_region( $bucket ) {
1083
+ try {
1084
+ $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
1085
+ }
1086
+ catch ( Exception $e ) {
1087
+ return new WP_Error( 'exception', $e->getMessage() );
1088
+ }
1089
+
1090
+ $region = $this->translate_region( $region['Location'] );
1091
+
1092
+ return $region;
1093
+ }
1094
+
1095
+ /**
1096
+ * Get the region of the bucket stored in the S3 metadata.
1097
  *
1098
+ *
1099
+ * @param array $s3object
1100
+ * @param int $post_id - if supplied will update the s3 meta if no region found
1101
+ *
1102
+ * @return string|WP_Error - region name
1103
  */
1104
  function get_s3object_region( $s3object, $post_id = null ) {
1105
  if ( ! isset( $s3object['region'] ) ) {
1106
  // if region hasn't been stored in the s3 metadata retrieve using the bucket
1107
+ $region = $this->get_bucket_region( $s3object['bucket'] );
1108
+ if ( is_wp_error( $region ) ) {
1109
+ return $region;
 
 
1110
  }
1111
+
1112
+ $s3object['region'] = $region;
1113
 
1114
  if ( ! is_null( $post_id ) ) {
1115
  // retrospectively update s3 metadata with region
1145
  *
1146
  * This is needed for non US standard buckets to add and delete files.
1147
  *
1148
+ * @param array $s3object
1149
+ * @param int $post_id
1150
  *
1151
  * @return string - region name
1152
  */
1165
  return $region;
1166
  }
1167
 
1168
+ /**
1169
+ * AJAX handler for get_buckets()
1170
+ */
1171
+ function ajax_get_buckets() {
1172
+ $this->verify_ajax_request();
1173
+
1174
+ $result = $this->get_buckets();
1175
+ if ( is_wp_error( $result ) ) {
1176
+ $out = array( 'error' => $result->get_error_message() );
1177
+ } else {
1178
+ $out = array(
1179
+ 'success' => '1',
1180
+ 'buckets' => $result,
1181
+ 'selected' => $this->get_setting( 'bucket' )
1182
+ );
1183
+ }
1184
+
1185
+ echo json_encode( $out );
1186
+ exit;
1187
+ }
1188
+
1189
+ /**
1190
+ * Get a list of buckets from S3
1191
+ *
1192
+ * @return array|WP_Error - list of buckets
1193
+ */
1194
  function get_buckets() {
1195
  try {
1196
  $result = $this->get_s3client()->listBuckets();
1205
  /**
1206
  * Checks the user has write permission for S3
1207
  *
1208
+ * @param string $bucket
1209
+ * @param string $region
1210
+ *
1211
  * @return bool
1212
  */
1213
+ function check_write_permission( $bucket = null, $region = null ) {
1214
+ if ( is_null( $bucket ) ) {
1215
+ if ( ! ( $bucket = $this->get_setting( 'bucket' ) ) ) {
1216
+ // if no bucket set then no need check
1217
+ return true;
1218
+ }
 
 
 
 
1219
  }
1220
 
 
1221
  $file_name = 'as3cf-permission-check.txt';
 
1222
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'as3cf' );
 
 
 
 
 
1223
 
1224
  $path = $this->get_setting( 'object-prefix' );
1225
+ $key = $path . $file_name;
1226
 
1227
  $args = array(
1228
+ 'Bucket' => $bucket,
1229
+ 'Key' => $key,
1230
+ 'Body' => $file_contents,
1231
+ 'ACL' => 'public-read',
1232
  );
1233
 
1234
  try {
1235
  // need to set region for buckets in non default region
1236
+ if ( is_null( $region ) ) {
1237
+ $region = $this->get_setting( 'region' );
1238
+ }
1239
+ if ( $region ) {
1240
  $this->get_s3client()->setRegion( $region );
1241
  }
1242
  // attempt to create the test file
1243
  $this->get_s3client()->putObject( $args );
1244
  // delete it straight away if created
1245
  $this->get_s3client()->deleteObject( array(
1246
+ 'Bucket' => $bucket,
1247
+ 'Key' => $key,
1248
+ ) );
1249
  $can_write = true;
1250
  } catch ( Exception $e ) {
1251
  // write permission not found
1252
  $can_write = false;
1253
  }
1254
 
 
 
 
1255
  return $can_write;
1256
  }
1257
 
1267
  wp_enqueue_script( 'as3cf-script', $src, array( 'jquery' ), $version, true );
1268
 
1269
  wp_localize_script( 'as3cf-script', 'as3cf_i18n', array(
1270
+ 'create_bucket_prompt' => __( 'Bucket Name:', 'as3cf' ),
1271
+ 'create_bucket_error' => __( 'Error creating bucket: ', 'as3cf' ),
1272
+ 'create_bucket_nonce' => wp_create_nonce( 'as3cf-create-bucket' ),
1273
+ 'get_buckets_error' => __( 'Error fetching buckets: ', 'as3cf' ),
1274
+ 'get_buckets_nonce' => wp_create_nonce( 'as3cf-get-buckets' ),
1275
+ 'save_bucket_error' => __( 'Error saving bucket: ', 'as3cf' ),
1276
+ 'save_bucket_nonce' => wp_create_nonce( 'as3cf-save-bucket' ),
1277
+ 'get_url_preview_nonce' => wp_create_nonce( 'as3cf-get-url-preview' ),
1278
+ 'get_url_preview_error' => __( 'Error getting URL preview: ', 'as3cf' ),
1279
+ 'save_alert' => __( 'The changes you made will be lost if you navigate away from this page', 'as3cf' ),
1280
+ ) );
1281
 
1282
  $this->handle_post_request();
1283
  }
1284
 
1285
  function handle_post_request() {
1286
+ if ( empty( $_POST['action'] ) || 'save' != sanitize_key( $_POST['action'] ) ) { // input var okay
1287
  return;
1288
  }
1289
 
1290
+ if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'as3cf-save-settings' ) ) { // input var okay
1291
  die( __( "Cheatin' eh?", 'amazon-web-services' ) );
1292
  }
1293
 
1294
+ $post_vars = array( 'domain', 'virtual-host', 'expires', 'permissions', 'cloudfront', 'object-prefix', 'copy-to-s3', 'serve-from-s3', 'remove-local-file', 'ssl', 'hidpi-images', 'object-versioning', 'use-yearmonth-folders', 'enable-object-prefix' );
1295
 
1296
  foreach ( $post_vars as $var ) {
1297
  $this->remove_setting( $var );
1298
 
1299
+ if ( ! isset( $_POST[ $var ] ) ) { // input var okay
1300
  continue;
1301
  }
1302
 
1303
+ $value = sanitize_text_field( $_POST[ $var ] ); // input var okay
1304
+
1305
+ $this->set_setting( $var, $value );
1306
  }
1307
 
1308
  $this->save_settings();
1327
  $this->aws->render_view( 'footer' );
1328
  }
1329
 
1330
+ /**
1331
+ * Get the prefix path for the files
1332
+ *
1333
+ * @param string $time
1334
+ *
1335
+ * @return string
1336
+ */
1337
  function get_dynamic_prefix( $time = null ) {
1338
  $uploads = wp_upload_dir( $time );
1339
+ $prefix = '';
1340
+ if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
1341
+ $prefix = str_replace( $this->get_base_upload_path(), '', $uploads['path'] );
1342
+ }
1343
+
1344
+ // support legacy MS installs (<3.5 since upgraded) for subsites
1345
+ if ( is_multisite() && 1 != ( $blog_id = get_current_blog_id() ) && strpos( $prefix, 'sites/' ) === false ) {
1346
+ $details = get_blog_details( $blog_id );
1347
+ $sitename = basename( $details->siteurl );
1348
+ $legacy_ms_prefix = $sitename . '/files/';
1349
+ $legacy_ms_prefix = apply_filters( 'as3cf_legacy_ms_subsite_prefix', $legacy_ms_prefix, $details );
1350
+ $prefix = '/' . trailingslashit( ltrim( $legacy_ms_prefix, '/' ) ) . ltrim( $prefix, '/' );
1351
+ }
1352
+
1353
+ return $prefix;
1354
  }
1355
 
1356
+ /**
1357
+ * Get the base upload path
1358
+ * without the multisite subdirectory
1359
+ *
1360
+ * @return string
1361
+ */
1362
  function get_base_upload_path() {
1363
  if ( defined( 'UPLOADS' ) && ! ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) ) {
1364
  return ABSPATH . UPLOADS;
1386
  'limit' => false,
1387
  'spam' => 0,
1388
  'deleted' => 0,
1389
+ 'archived' => 0,
1390
  );
1391
  $blogs = wp_get_sites( $args );
1392
 
classes/as3cf-upgrade.php CHANGED
@@ -37,7 +37,7 @@ class AS3CF_Upgrade {
37
  /**
38
  * Start it up
39
  *
40
- * @param unknown $as3cf - the instance of the as3cf class
41
  */
42
  function __construct( $as3cf ) {
43
  $this->as3cf = $as3cf;
@@ -82,33 +82,39 @@ class AS3CF_Upgrade {
82
  * Adds notices about issues with upgrades allowing user to restart them
83
  */
84
  function maybe_display_notices() {
85
- $restart_url = self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=restart_update_meta_with_region' );
 
86
 
87
  switch ( $this->get_upgrade_status() ) {
88
  case self::STATUS_RUNNING :
89
- $msg = sprintf( __( '<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 and updating the metadata with the bucket region it is served from. This will allow us to serve your files from the proper S3 region subdomain <span style="white-space:nowrap;">(e.g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the background, processing a small batch of Media Library items every %d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'as3cf' ), $this->cron_interval_in_minutes );
90
- $msg .= ' <strong><a href="' . self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=pause_update_meta_with_region' ) . '">' . __( 'Pause Update', 'as3cf' ) . '</a></strong>';
91
- $this->as3cf->render_view( 'notice', array( 'message' => $msg ) );
92
  break;
93
  case self::STATUS_PAUSED :
94
- $msg = __( '<strong>Metadata Update Paused</strong> &mdash; Updating Media Library metadata has been paused.', 'as3cf' );
95
- $msg .= ' <strong><a href="' . $restart_url . '">' . __( 'Restart Update', 'as3cf' ) . '</a></strong>';
96
- $this->as3cf->render_view( 'notice', array( 'message' => $msg ) );
97
  break;
98
  case self::STATUS_ERROR :
99
- $msg = __( '<strong>Error Updating Metadata</strong> &mdash; We ran into some errors attempting to update the metadata for all your Media Library items that have been uploaded to S3. Please check your error log for details.', 'as3cf' );
100
- $msg .= ' <strong><a href="' . $restart_url . '">' . __( 'Try Run It Again', 'as3cf' ) . '</a></strong>';
101
- $this->as3cf->render_view( 'error', array( 'message' => $msg ) );
102
  break;
 
 
103
  }
 
 
 
 
104
  }
105
 
106
  function maybe_handle_action() {
107
- if ( ! isset( $_GET['page'] ) || $_GET['page'] != $this->as3cf->get_plugin_slug() || ! isset( $_GET['action'] ) ) {
108
  return;
109
  }
110
 
111
- $method_name = 'action_' . $_GET['action'];
112
  if ( method_exists( $this, $method_name ) ) {
113
  call_user_func( array( $this, $method_name ) );
114
  }
@@ -132,6 +138,8 @@ class AS3CF_Upgrade {
132
 
133
  /**
134
  * Helper for the above action requests
 
 
135
  */
136
  function change_status_request( $status ) {
137
  $session = $this->get_session();
@@ -152,7 +160,7 @@ class AS3CF_Upgrade {
152
  // Adds every 10 minutes to the existing schedules.
153
  $schedules[ self::CRON_SCHEDULE_KEY ] = array(
154
  'interval' => $this->cron_interval_in_minutes * 60,
155
- 'display' => __( 'Every ' . $this->cron_interval_in_minutes . ' Minutes', 'as3cf' )
156
  );
157
 
158
  return $schedules;
@@ -270,7 +278,7 @@ class AS3CF_Upgrade {
270
  if ( time() >= $finish ) {
271
  break;
272
  }
273
-
274
  $s3object = unserialize( $attachment->s3object );
275
  if ( false === $s3object ) {
276
  error_log( 'Failed to unserialize S3 meta for attachment ' . $attachment->ID . ': ' . $attachment->s3object );
37
  /**
38
  * Start it up
39
  *
40
+ * @param Amazon_S3_And_CloudFront $as3cf - the instance of the as3cf class
41
  */
42
  function __construct( $as3cf ) {
43
  $this->as3cf = $as3cf;
82
  * Adds notices about issues with upgrades allowing user to restart them
83
  */
84
  function maybe_display_notices() {
85
+ $action_url = self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=restart_update_meta_with_region' );
86
+ $msg_type = 'notice';
87
 
88
  switch ( $this->get_upgrade_status() ) {
89
  case self::STATUS_RUNNING :
90
+ $msg = sprintf( __( '<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 and updating the metadata with the bucket region it is served from. This will allow us to serve your files from the proper S3 region subdomain <span style="white-space:nowrap;">(e.g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the background, processing a small batch of Media Library items every %d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'as3cf' ), $this->cron_interval_in_minutes );
91
+ $action_text = __( 'Pause Update', 'as3cf' );
92
+ $action_url = self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=pause_update_meta_with_region' );
93
  break;
94
  case self::STATUS_PAUSED :
95
+ $msg = __( '<strong>Metadata Update Paused</strong> &mdash; Updating Media Library metadata has been paused.', 'as3cf' );
96
+ $action_text = __( 'Restart Update', 'as3cf' );
 
97
  break;
98
  case self::STATUS_ERROR :
99
+ $msg = __( '<strong>Error Updating Metadata</strong> &mdash; We ran into some errors attempting to update the metadata for all your Media Library items that have been uploaded to S3. Please check your error log for details.', 'as3cf' );
100
+ $action_text = __( 'Try Run It Again', 'as3cf' );
101
+ $msg_type = 'error';
102
  break;
103
+ default :
104
+ return;
105
  }
106
+
107
+ $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
108
+
109
+ $this->as3cf->render_view( $msg_type, array( 'message' => $msg ) );
110
  }
111
 
112
  function maybe_handle_action() {
113
+ if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) != $this->as3cf->get_plugin_slug() || ! isset( $_GET['action'] ) ) { // input var okay
114
  return;
115
  }
116
 
117
+ $method_name = 'action_' . sanitize_key( $_GET['action'] ); // input var okay
118
  if ( method_exists( $this, $method_name ) ) {
119
  call_user_func( array( $this, $method_name ) );
120
  }
138
 
139
  /**
140
  * Helper for the above action requests
141
+ *
142
+ * @param integer $status
143
  */
144
  function change_status_request( $status ) {
145
  $session = $this->get_session();
160
  // Adds every 10 minutes to the existing schedules.
161
  $schedules[ self::CRON_SCHEDULE_KEY ] = array(
162
  'interval' => $this->cron_interval_in_minutes * 60,
163
+ 'display' => sprintf( __( 'Every %d Minutes', 'as3cf' ), $this->cron_interval_in_minutes ),
164
  );
165
 
166
  return $schedules;
278
  if ( time() >= $finish ) {
279
  break;
280
  }
281
+
282
  $s3object = unserialize( $attachment->s3object );
283
  if ( false === $s3object ) {
284
  error_log( 'Failed to unserialize S3 meta for attachment ' . $attachment->ID . ': ' . $attachment->s3object );
languages/amazon-s3-and-cloudfront.pot CHANGED
@@ -1,8 +1,8 @@
1
  msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Amazon S3 and CloudFront\n"
4
- "POT-Creation-Date: 2014-12-11 15:33-0000\n"
5
- "PO-Revision-Date: 2014-12-11 15:33-0000\n"
6
  "Last-Translator: Delicious Brains <nom@deliciousbrains.com>\n"
7
  "Language-Team: Delicious Brains <nom@deliciousbrains.com>\n"
8
  "Language: en\n"
@@ -28,44 +28,58 @@ msgstr ""
28
  msgid "S3 and CloudFront"
29
  msgstr ""
30
 
31
- #: classes/amazon-s3-and-cloudfront.php:666
 
 
 
 
32
  msgid "Cheatin&#8217; eh?"
33
  msgstr ""
34
 
35
- #: classes/amazon-s3-and-cloudfront.php:670
36
  msgid "You do not have sufficient permissions to access this page."
37
  msgstr ""
38
 
39
- #: classes/amazon-s3-and-cloudfront.php:678
40
  msgid "No bucket name provided."
41
  msgstr ""
42
 
43
- #: classes/amazon-s3-and-cloudfront.php:816
44
- msgid ""
45
- "There was an error attempting to access the local file system whilst "
46
- "checking the bucket permissions"
47
  msgstr ""
48
 
49
- #: classes/amazon-s3-and-cloudfront.php:822
50
  msgid ""
51
  "This is a test file to check if the user has write permission to S3. Delete "
52
  "me if found."
53
  msgstr ""
54
 
55
- #: classes/amazon-s3-and-cloudfront.php:826
56
- msgid ""
57
- "It looks like we cannot create a file locally to test the S3 permissions"
58
- msgstr ""
59
-
60
- #: classes/amazon-s3-and-cloudfront.php:877
61
  msgid "Bucket Name:"
62
  msgstr ""
63
 
64
- #: classes/amazon-s3-and-cloudfront.php:878
65
  msgid "Error creating bucket: "
66
  msgstr ""
67
 
68
- #: classes/amazon-s3-and-cloudfront.php:891
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  msgid "Cheatin' eh?"
70
  msgstr ""
71
 
@@ -126,7 +140,7 @@ msgstr ""
126
  msgid "Update Amazon S3 and CloudFront to the latest version"
127
  msgstr ""
128
 
129
- #: classes/as3cf-upgrade.php:89
130
  #, php-format
131
  msgid ""
132
  "<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through "
@@ -139,17 +153,17 @@ msgid ""
139
  "performance."
140
  msgstr ""
141
 
142
- #: classes/as3cf-upgrade.php:90
143
  msgid "Pause Update"
144
  msgstr ""
145
 
146
- #: classes/as3cf-upgrade.php:94
147
  msgid ""
148
  "<strong>Metadata Update Paused</strong> &mdash; Updating Media Library "
149
  "metadata has been paused."
150
  msgstr ""
151
 
152
- #: classes/as3cf-upgrade.php:95
153
  msgid "Restart Update"
154
  msgstr ""
155
 
@@ -164,113 +178,198 @@ msgstr ""
164
  msgid "Try Run It Again"
165
  msgstr ""
166
 
167
- #: classes/as3cf-upgrade.php:155
168
- msgid "Every "
169
- msgstr ""
170
-
171
- #: view/settings.php:10
172
- msgid "Error retrieving a list of your S3 buckets from AWS:"
173
  msgstr ""
174
 
175
- #: view/settings.php:21
176
  msgid "Settings saved."
177
  msgstr ""
178
 
179
- #: view/settings.php:40
180
  msgid "S3 Policy is Read-Only"
181
  msgstr ""
182
 
183
- #: view/settings.php:42
184
  #, php-format
185
  msgid ""
186
- "You need to go to <a href=\"%s\">Identity and Access Management</a> in your "
187
  "AWS console and manage the policy for the user you're using for this plugin. "
188
  "Your policy should look something like the following:"
189
  msgstr ""
190
 
191
- #: view/settings.php:66
192
- msgid "S3 Settings"
 
 
 
 
 
 
 
 
 
 
 
 
193
  msgstr ""
194
 
195
- #: view/settings.php:69
196
- msgid "Select an S3 Bucket"
197
  msgstr ""
198
 
199
- #: view/settings.php:73
200
- msgid "Create a new bucket..."
201
  msgstr ""
202
 
203
- #: view/settings.php:77
204
- msgid "Bucket is setup for virtual hosting"
205
  msgstr ""
206
 
207
- #: view/settings.php:77
208
- msgid "more info"
 
 
 
 
209
  msgstr ""
210
 
211
  #: view/settings.php:81
212
- #, php-format
213
- msgid ""
214
- "Set a <a href=\"%s\" target=\"_blank\">far future HTTP expiration header</a> "
215
- "for uploaded files <em>(recommended)</em>"
216
  msgstr ""
217
 
218
  #: view/settings.php:87
219
- msgid "Object Path:"
 
 
 
 
220
  msgstr ""
221
 
222
  #: view/settings.php:95
223
- msgid "CloudFront Settings"
 
 
224
  msgstr ""
225
 
226
- #: view/settings.php:97
227
- msgid "Domain Name"
228
  msgstr ""
229
 
230
- #: view/settings.php:99
231
- msgid "Leave blank if you aren&#8217;t using CloudFront."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  msgstr ""
233
 
234
- #: view/settings.php:106
235
- msgid "Plugin Settings"
236
  msgstr ""
237
 
238
- #: view/settings.php:109
239
- msgid "Copy files to S3 as they are uploaded to the Media Library"
240
  msgstr ""
241
 
242
- #: view/settings.php:113
243
- msgid "Point file URLs to S3/CloudFront for files that have been copied to S3"
244
  msgstr ""
245
 
246
- #: view/settings.php:114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  msgid ""
248
- "When this is option unchecked, your Media Library images and other files "
249
- "will be served from your server instead of S3/CloudFront. If any images were "
250
- "removed from your server, they will be broken."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  msgstr ""
252
 
253
- #: view/settings.php:121
 
 
 
 
254
  msgid ""
255
- "Remove uploaded file from local filesystem once it has been copied to S3"
 
256
  msgstr ""
257
 
258
- #: view/settings.php:125
259
- msgid "Always serve files over https (SSL)"
260
  msgstr ""
261
 
262
- #: view/settings.php:129
263
- #, php-format
 
 
 
264
  msgid ""
265
- "Implement <a href=\"%s\">object versioning</a> by appending a timestamp to "
266
- "the S3 file path"
 
267
  msgstr ""
268
 
269
- #: view/settings.php:133
270
- msgid "Copy any HiDPI (@2x) images to S3 (works with WP Retina 2x plugin)"
 
 
 
 
 
 
 
 
271
  msgstr ""
272
 
273
- #: view/settings.php:139
274
  msgid "Save Changes"
275
  msgstr ""
276
 
1
  msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Amazon S3 and CloudFront\n"
4
+ "POT-Creation-Date: 2015-01-09 16:19-0000\n"
5
+ "PO-Revision-Date: 2015-01-09 16:19-0000\n"
6
  "Last-Translator: Delicious Brains <nom@deliciousbrains.com>\n"
7
  "Language-Team: Delicious Brains <nom@deliciousbrains.com>\n"
8
  "Language: en\n"
28
  msgid "S3 and CloudFront"
29
  msgstr ""
30
 
31
+ #: classes/amazon-s3-and-cloudfront.php:920
32
+ msgid "There was an error attempting to access the file system"
33
+ msgstr ""
34
+
35
+ #: classes/amazon-s3-and-cloudfront.php:960
36
  msgid "Cheatin&#8217; eh?"
37
  msgstr ""
38
 
39
+ #: classes/amazon-s3-and-cloudfront.php:964
40
  msgid "You do not have sufficient permissions to access this page."
41
  msgstr ""
42
 
43
+ #: classes/amazon-s3-and-cloudfront.php:970
44
  msgid "No bucket name provided."
45
  msgstr ""
46
 
47
+ #: classes/amazon-s3-and-cloudfront.php:996
48
+ #: classes/amazon-s3-and-cloudfront.php:1029
49
+ msgid "Failed to retrieve bucket region."
 
50
  msgstr ""
51
 
52
+ #: classes/amazon-s3-and-cloudfront.php:1222
53
  msgid ""
54
  "This is a test file to check if the user has write permission to S3. Delete "
55
  "me if found."
56
  msgstr ""
57
 
58
+ #: classes/amazon-s3-and-cloudfront.php:1270
 
 
 
 
 
59
  msgid "Bucket Name:"
60
  msgstr ""
61
 
62
+ #: classes/amazon-s3-and-cloudfront.php:1271
63
  msgid "Error creating bucket: "
64
  msgstr ""
65
 
66
+ #: classes/amazon-s3-and-cloudfront.php:1273
67
+ msgid "Error fetching buckets: "
68
+ msgstr ""
69
+
70
+ #: classes/amazon-s3-and-cloudfront.php:1275
71
+ msgid "Error saving bucket: "
72
+ msgstr ""
73
+
74
+ #: classes/amazon-s3-and-cloudfront.php:1278
75
+ msgid "Error getting URL preview: "
76
+ msgstr ""
77
+
78
+ #: classes/amazon-s3-and-cloudfront.php:1279
79
+ msgid "The changes you made will be lost if you navigate away from this page"
80
+ msgstr ""
81
+
82
+ #: classes/amazon-s3-and-cloudfront.php:1291
83
  msgid "Cheatin' eh?"
84
  msgstr ""
85
 
140
  msgid "Update Amazon S3 and CloudFront to the latest version"
141
  msgstr ""
142
 
143
+ #: classes/as3cf-upgrade.php:90
144
  #, php-format
145
  msgid ""
146
  "<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through "
153
  "performance."
154
  msgstr ""
155
 
156
+ #: classes/as3cf-upgrade.php:91
157
  msgid "Pause Update"
158
  msgstr ""
159
 
160
+ #: classes/as3cf-upgrade.php:95
161
  msgid ""
162
  "<strong>Metadata Update Paused</strong> &mdash; Updating Media Library "
163
  "metadata has been paused."
164
  msgstr ""
165
 
166
+ #: classes/as3cf-upgrade.php:96
167
  msgid "Restart Update"
168
  msgstr ""
169
 
178
  msgid "Try Run It Again"
179
  msgstr ""
180
 
181
+ #: classes/as3cf-upgrade.php:163
182
+ #, php-format
183
+ msgid "Every %d Minutes"
 
 
 
184
  msgstr ""
185
 
186
+ #: view/settings.php:19
187
  msgid "Settings saved."
188
  msgstr ""
189
 
190
+ #: view/settings.php:35
191
  msgid "S3 Policy is Read-Only"
192
  msgstr ""
193
 
194
+ #: view/settings.php:37
195
  #, php-format
196
  msgid ""
197
+ "You need to go to <a href=\"%s\">Identity and Access Management</a> in your "
198
  "AWS console and manage the policy for the user you're using for this plugin. "
199
  "Your policy should look something like the following:"
200
  msgstr ""
201
 
202
+ #: view/settings.php:52
203
+ msgid "Select an existing S3 bucket to use:"
204
+ msgstr ""
205
+
206
+ #: view/settings.php:55
207
+ msgid "Cancel"
208
+ msgstr ""
209
+
210
+ #: view/settings.php:57
211
+ msgid "Refresh"
212
+ msgstr ""
213
+
214
+ #: view/settings.php:60
215
+ msgid "Loading..."
216
  msgstr ""
217
 
218
+ #: view/settings.php:63
219
+ msgid "Or create a new bucket:"
220
  msgstr ""
221
 
222
+ #: view/settings.php:66
223
+ msgid "Bucket Name"
224
  msgstr ""
225
 
226
+ #: view/settings.php:67
227
+ msgid "Creating..."
228
  msgstr ""
229
 
230
+ #: view/settings.php:67
231
+ msgid "Create"
232
+ msgstr ""
233
+
234
+ #: view/settings.php:78
235
+ msgid "Bucket"
236
  msgstr ""
237
 
238
  #: view/settings.php:81
239
+ msgid "Change"
 
 
 
240
  msgstr ""
241
 
242
  #: view/settings.php:87
243
+ msgid "Enable/Disable the Plugin"
244
+ msgstr ""
245
+
246
+ #: view/settings.php:94
247
+ msgid "Copy Files to S3"
248
  msgstr ""
249
 
250
  #: view/settings.php:95
251
+ msgid ""
252
+ "When a file is uploaded to the Media Library, copy it to S3. Existing files "
253
+ "are <em>not</em> copied to S3."
254
  msgstr ""
255
 
256
+ #: view/settings.php:103
257
+ msgid "Rewrite File URLs"
258
  msgstr ""
259
 
260
+ #: view/settings.php:104
261
+ msgid ""
262
+ "For Media Library files that have been copied to S3, rewrite the URLs so "
263
+ "that they are served from S3/CloudFront instead of your server."
264
+ msgstr ""
265
+
266
+ #: view/settings.php:108
267
+ msgid "Configure File URLs"
268
+ msgstr ""
269
+
270
+ #: view/settings.php:122
271
+ msgid "Domain:"
272
+ msgstr ""
273
+
274
+ #: view/settings.php:168
275
+ msgid "Path"
276
+ msgstr ""
277
+
278
+ #: view/settings.php:170
279
+ msgid "By default the path is the same as your local WordPress files:"
280
  msgstr ""
281
 
282
+ #: view/settings.php:183
283
+ msgid "Year/Month"
284
  msgstr ""
285
 
286
+ #: view/settings.php:185
287
+ msgid "Add the Year/Month in the URL."
288
  msgstr ""
289
 
290
+ #: view/settings.php:191
291
+ msgid "SSL"
292
  msgstr ""
293
 
294
+ #: view/settings.php:199
295
+ msgid "Same as request"
296
+ msgstr ""
297
+
298
+ #: view/settings.php:200
299
+ msgid "When the request is https://, use https:// for the file URL as well."
300
+ msgstr ""
301
+
302
+ #: view/settings.php:204
303
+ msgid "Always SSL"
304
+ msgstr ""
305
+
306
+ #: view/settings.php:205
307
+ msgid "Forces https:// to be used."
308
+ msgstr ""
309
+
310
+ #: view/settings.php:206
311
  msgid ""
312
+ "You cannot use the \"Bucket as a subdomain\" domain option when using SSL."
313
+ msgstr ""
314
+
315
+ #: view/settings.php:210
316
+ msgid "Always non-SSL"
317
+ msgstr ""
318
+
319
+ #: view/settings.php:211
320
+ msgid "Forces http:// to be used."
321
+ msgstr ""
322
+
323
+ #: view/settings.php:217
324
+ msgid "Advanced Options"
325
+ msgstr ""
326
+
327
+ #: view/settings.php:224
328
+ msgid "Remove Files From Server"
329
+ msgstr ""
330
+
331
+ #: view/settings.php:225
332
+ msgid "Once a file has been copied to S3, remove it from the local server."
333
  msgstr ""
334
 
335
+ #: view/settings.php:233
336
+ msgid "Object Versioning"
337
+ msgstr ""
338
+
339
+ #: view/settings.php:235
340
  msgid ""
341
+ "Append a timestamp to the S3 file path. Recommended when using CloudFront so "
342
+ "you don't have to worry about cache invalidation."
343
  msgstr ""
344
 
345
+ #: view/settings.php:237 view/settings.php:250
346
+ msgid "More info"
347
  msgstr ""
348
 
349
+ #: view/settings.php:247
350
+ msgid "Far Future Expiration Header"
351
+ msgstr ""
352
+
353
+ #: view/settings.php:248
354
  msgid ""
355
+ "Implements a \"Never Expire\" caching policy for browsers by setting an "
356
+ "Expires header for 10 years in the future. Should be used in conjunction "
357
+ "with object versioning above."
358
  msgstr ""
359
 
360
+ #: view/settings.php:260
361
+ msgid "Copy HiDPI (@2x) Images"
362
+ msgstr ""
363
+
364
+ #: view/settings.php:261
365
+ #, php-format
366
+ msgid ""
367
+ "When uploading a file to S3, checks if there's a file of the same name with "
368
+ "an @2x suffix and copies it to S3 as well. Works with the <a href=\"%s\">WP "
369
+ "Retina 2x</a> plugin."
370
  msgstr ""
371
 
372
+ #: view/settings.php:267
373
  msgid "Save Changes"
374
  msgstr ""
375
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
4
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  Requires at least: 3.5
6
  Tested up to: 4.1
7
- Stable tag: 0.7.2
8
  License: GPLv3
9
 
10
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
@@ -43,7 +43,8 @@ which is a fork of [Amazon S3 for WordPress](http://wordpress.org/extend/plugins
43
 
44
  == Screenshots ==
45
 
46
- 1. Settings screen
 
47
 
48
  == Upgrade Notice ==
49
 
@@ -58,6 +59,15 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
58
 
59
  == Changelog ==
60
 
 
 
 
 
 
 
 
 
 
61
  = 0.7.2 - 2014-12-11 =
62
  * Bug: Some buckets in the EU region causing permission and HTTP errors
63
  * Bug: Undefined variable: message in view/error.php also causing white screens
4
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  Requires at least: 3.5
6
  Tested up to: 4.1
7
+ Stable tag: 0.8
8
  License: GPLv3
9
 
10
  Copies files to Amazon S3 as they are uploaded to the Media Library. Optionally configure Amazon CloudFront for faster delivery.
43
 
44
  == Screenshots ==
45
 
46
+ 1. Choosing/creating a bucket
47
+ 2. Settings screen
48
 
49
  == Upgrade Notice ==
50
 
59
 
60
  == Changelog ==
61
 
62
+ = 0.8 - 2015-01-10 =
63
+ * New: Redesigned settings UI
64
+ * Improvement: SSL setting can be fully controlled, HTTPS for urls always, based on request or never
65
+ * Improvement: Download files from S3 that are not found on server when running Regenerate Thumbnails plugin
66
+ * Improvement: When calling `get_attached_file()` and file is missing from server, return S3 URL
67
+ * Improvement: Code cleanup to WordPress coding standards
68
+ * Bug Fix: Files for all subsites going into the same S3 folder on multisite installs setup prior to WP 3.5
69
+ * Bug Fix: 'attempting to access local file system' error for some installs
70
+
71
  = 0.7.2 - 2014-12-11 =
72
  * Bug: Some buckets in the EU region causing permission and HTTP errors
73
  * Bug: Undefined variable: message in view/error.php also causing white screens
view/checkbox.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $value = ( isset( $value ) ) ? $value : $this->get_setting( $key );
3
+ $class = ( isset( $class ) ) ? 'class="' . $class . '"' : '';
4
+ $disabled = ( isset( $disabled ) && $disabled ) ? 'disabled' : '';
5
+ ?>
6
+ <div id="<?php echo $key; ?>-wrap" data-checkbox="<?php echo $key; ?>" class="as3cf-switch <?php echo $disabled; ?>">
7
+ <span class="on <?php echo $value ? 'checked' : ''; ?>">ON</span>
8
+ <span class="off <?php echo ! $value ? 'checked' : ''; ?>">OFF</span>
9
+ <input type="hidden" name="<?php echo $key; ?>" value="0" />
10
+ <input type="checkbox" name="<?php echo $key; ?>" value="1" id="<?php echo $key; ?>" <?php echo $value ? 'checked="checked" ' : ''; ?> <?php echo $class ?>/>
11
+ </div>
view/error.php CHANGED
@@ -1,3 +1,3 @@
1
  <div class="error">
2
- <p><?php echo $message; ?></p>
3
  </div>
1
  <div class="error">
2
+ <p><?php echo $message; // xss ok ?></p>
3
  </div>
view/notice.php CHANGED
@@ -1,3 +1,3 @@
1
  <div class="updated as3cf-notice">
2
- <p><?php echo $message; ?></p>
3
  </div>
1
  <div class="updated as3cf-notice">
2
+ <p><?php echo $message; // xss ok ?></p>
3
  </div>
view/settings.php CHANGED
@@ -1,47 +1,42 @@
1
- <div class="aws-content as3cf-settings">
2
-
3
  <?php
4
- $buckets = $this->get_buckets();
 
5
 
6
- if ( is_wp_error( $buckets ) ) :
7
- ?>
8
- <div class="error">
9
  <p>
10
- <?php _e( 'Error retrieving a list of your S3 buckets from AWS:', 'as3cf' ); ?>
11
- <?php echo $buckets->get_error_message(); ?>
12
  </p>
13
  </div>
14
  <?php
15
- endif;
16
-
17
- if ( isset( $_GET['updated'] ) ) {
 
18
  ?>
19
- <div class="updated">
20
  <p>
21
  <?php _e( 'Settings saved.', 'as3cf' ); ?>
22
  </p>
23
  </div>
24
- <?php
25
- }
26
 
27
- $can_write = true;
28
- if ( ! is_wp_error( $buckets ) && is_array( $buckets ) ) {
29
  $can_write = $this->check_write_permission();
30
  // catch any file system issues
31
- if ( is_wp_error( $can_write ) ) {
32
- $this->render_view( 'error', array( 'message' => $can_write->get_error_message() ) );
33
- }
34
  }
35
  // display a error message if the user does not have write permission to S3
36
- if ( ! $can_write ) : ?>
37
- <div class="error">
38
  <p>
39
  <strong>
40
  <?php _e( 'S3 Policy is Read-Only', 'as3cf' ); ?>
41
  </strong>&mdash;
42
- <?php printf( __( 'You need to go to <a href="%s">Identity and Access Management</a> in your AWS console and manage the policy for the user you\'re using for this plugin. Your policy should look something like the following:', 'as3cf' ), 'https://console.aws.amazon.com/iam/home' ); ?>
43
  </p>
44
- <pre><code>{
45
  "Version": "2012-10-17",
46
  "Statement": [
47
  {
@@ -52,97 +47,228 @@ if ( ! $can_write ) : ?>
52
  ]
53
  }</code></pre>
54
  </div>
55
- <?php
56
- endif;
57
- ?>
58
-
59
- <form method="post">
60
- <input type="hidden" name="action" value="save" />
61
- <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
62
-
63
- <table class="form-table">
64
- <tr valign="top">
65
- <td>
66
- <h3><?php _e( 'S3 Settings', 'as3cf' ); ?></h3>
67
-
68
- <select name="bucket" class="bucket">
69
- <option value="">-- <?php _e( 'Select an S3 Bucket', 'as3cf' ); ?> --</option>
70
- <?php if ( is_array( $buckets ) ) foreach ( $buckets as $bucket ): ?>
71
- <option value="<?php echo esc_attr( $bucket['Name'] ); ?>" <?php echo $bucket['Name'] == $this->get_setting( 'bucket' ) ? 'selected="selected"' : ''; ?>><?php echo esc_html( $bucket['Name'] ); ?></option>
72
- <?php endforeach;?>
73
- <option value="new"><?php _e( 'Create a new bucket...', 'as3cf' ); ?></option>
74
- </select><br />
75
 
76
- <input type="checkbox" name="virtual-host" value="1" id="virtual-host" <?php echo $this->get_setting( 'virtual-host' ) ? 'checked="checked" ' : '';?> />
77
- <label for="virtual-host"> <?php _e( 'Bucket is setup for virtual hosting', 'as3cf' ); ?></label> (<a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html"><?php _e( 'more info', 'as3cf' ); ?></a>)
78
- <br />
79
-
80
- <input type="checkbox" name="expires" value="1" id="expires" <?php echo $this->get_setting( 'expires' ) ? 'checked="checked" ' : ''; ?> />
81
- <label for="expires"> <?php printf( __( 'Set a <a href="%s" target="_blank">far future HTTP expiration header</a> for uploaded files <em>(recommended)</em>', 'as3cf' ), 'http://developer.yahoo.com/performance/rules.html#expires' ); ?></label>
82
- </td>
83
- </tr>
84
-
85
- <tr valign="top">
86
- <td>
87
- <label><?php _e( 'Object Path:', 'as3cf' ); ?></label>&nbsp;&nbsp;
88
- <input type="text" name="object-prefix" value="<?php echo esc_attr( $this->get_setting( 'object-prefix' ) ); ?>" size="30" />
89
- <label><?php echo trailingslashit( $this->get_dynamic_prefix() ); ?></label>
90
- </td>
91
- </tr>
92
-
93
- <tr valign="top">
94
- <td>
95
- <h3><?php _e( 'CloudFront Settings', 'as3cf' ); ?></h3>
96
-
97
- <label><?php _e( 'Domain Name', 'as3cf' ); ?></label><br />
98
- <input type="text" name="cloudfront" value="<?php echo esc_attr( $this->get_setting( 'cloudfront' ) ); ?>" size="50" />
99
- <p class="description"><?php _e( 'Leave blank if you aren&#8217;t using CloudFront.', 'as3cf' ); ?></p>
100
-
101
- </td>
102
- </tr>
103
-
104
- <tr valign="top">
105
- <td>
106
- <h3><?php _e( 'Plugin Settings', 'as3cf' ); ?></h3>
107
-
108
- <input type="checkbox" name="copy-to-s3" value="1" id="copy-to-s3" <?php echo $this->get_setting( 'copy-to-s3' ) ? 'checked="checked" ' : ''; ?> />
109
- <label for="copy-to-s3"> <?php _e( 'Copy files to S3 as they are uploaded to the Media Library', 'as3cf' ); ?></label>
110
- <br />
111
-
112
- <input type="checkbox" name="serve-from-s3" value="1" id="serve-from-s3" <?php echo $this->get_setting( 'serve-from-s3' ) ? 'checked="checked" ' : ''; ?> />
113
- <label for="serve-from-s3"> <?php _e( 'Point file URLs to S3/CloudFront for files that have been copied to S3', 'as3cf' ); ?>
114
- <span class="tooltip" data-tooltip="<?php _e( 'When this is option unchecked, your Media Library images and other files will be served from your server instead of S3/CloudFront. If any images were removed from your server, they will be broken.', 'as3cf' ); ?>">
115
- <span class="dashicons dashicons-editor-help"></span>
116
  </span>
117
- </label>
118
- <br />
119
-
120
- <input type="checkbox" name="remove-local-file" value="1" id="remove-local-file" <?php echo $this->get_setting( 'remove-local-file' ) ? 'checked="checked" ' : ''; ?> />
121
- <label for="remove-local-file"> <?php _e( 'Remove uploaded file from local filesystem once it has been copied to S3', 'as3cf' ); ?></label>
122
- <br />
123
-
124
- <input type="checkbox" name="force-ssl" value="1" id="force-ssl" <?php echo $this->get_setting( 'force-ssl' ) ? 'checked="checked" ' : ''; ?> />
125
- <label for="force-ssl"> <?php _e( 'Always serve files over https (SSL)', 'as3cf' ); ?></label>
126
- <br />
127
-
128
- <input type="checkbox" name="object-versioning" value="1" id="object-versioning" <?php echo $this->get_setting( 'object-versioning' ) ? 'checked="checked" ' : ''; ?> />
129
- <label for="object-versioning"> <?php printf( __( 'Implement <a href="%s">object versioning</a> by appending a timestamp to the S3 file path', 'as3cf' ), 'http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ReplacingObjects.html' ); ?></label>
130
- <br />
131
 
132
- <input type="checkbox" name="hidpi-images" value="1" id="hidpi-images" <?php echo $this->get_setting( 'hidpi-images' ) ? 'checked="checked" ' : ''; ?> />
133
- <label for="hidpi-images"> <?php _e( 'Copy any HiDPI (@2x) images to S3 (works with WP Retina 2x plugin)', 'as3cf' ); ?></label>
 
 
134
 
135
- </td>
136
- </tr>
137
- <tr valign="top">
138
- <td>
139
- <button type="submit" class="button button-primary"><?php _e( 'Save Changes', 'amazon-web-services' ); ?></button>
140
- </td>
141
- </tr>
142
- </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
- </form>
 
 
 
 
 
145
 
146
- <?php $this->render_view( 'sidebar' ); ?>
147
 
148
  </div>
 
 
1
  <?php
2
+ $selected_bucket = $this->get_setting( 'bucket' ); ?>
3
+ <div class="aws-content as3cf-settings<?php echo ( $selected_bucket ) ? ' as3cf-has-bucket' : ''; // xss ok ?>">
4
 
5
+ <div class="error as3cf-bucket-error" style="display: none;">
 
 
6
  <p>
7
+ <span class="title"></span>
8
+ <span class="message"></span>
9
  </p>
10
  </div>
11
  <?php
12
+ $updated_class = '';
13
+ if ( isset( $_GET['updated'] ) ) { // input var okay
14
+ $updated_class = 'show';
15
+ }
16
  ?>
17
+ <div class="updated <?php echo $updated_class; // xss ok ?>">
18
  <p>
19
  <?php _e( 'Settings saved.', 'as3cf' ); ?>
20
  </p>
21
  </div>
 
 
22
 
23
+ <?php
 
24
  $can_write = $this->check_write_permission();
25
  // catch any file system issues
26
+ if ( is_wp_error( $can_write ) ) {
27
+ $this->render_view( 'error-fatal', array( 'message' => $can_write->get_error_message() ) );
28
+ $can_write = true;
29
  }
30
  // display a error message if the user does not have write permission to S3
31
+ ?>
32
+ <div class="error as3cf-can-write-error" style="<?php echo ( $can_write ) ? 'display: none;' : ''; // xss ok ?>">
33
  <p>
34
  <strong>
35
  <?php _e( 'S3 Policy is Read-Only', 'as3cf' ); ?>
36
  </strong>&mdash;
37
+ <?php printf( __( 'You need to go to <a href="%s">Identity and Access Management</a> in your AWS console and manage the policy for the user you\'re using for this plugin. Your policy should look something like the following:', 'as3cf' ), 'https://console.aws.amazon.com/iam/home' ); ?>
38
  </p>
39
+ <pre><code>{
40
  "Version": "2012-10-17",
41
  "Statement": [
42
  {
47
  ]
48
  }</code></pre>
49
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ <div class="as3cf-bucket-select">
52
+ <h3><?php _e( 'Select an existing S3 bucket to use:', 'as3cf' ); ?></h3>
53
+ <div class="as3cf-bucket-actions">
54
+ <span class="as3cf-cancel-bucket-select-wrap">
55
+ <a href="#" class="as3cf-cancel-bucket-select"><?php _e( 'Cancel', 'as3cf' ); ?></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  </span>
57
+ <a href="#" class="as3cf-refresh-buckets"><?php _e( 'Refresh', 'as3cf' ); ?></a>
58
+ </div>
59
+ <div class="as3cf-bucket-list-wrapper">
60
+ <ul class="as3cf-bucket-list" data-working="<?php _e( 'Loading...', 'as3cf' ); ?>">
61
+ </ul>
62
+ </div>
63
+ <h3><?php _e( 'Or create a new bucket:', 'as3cf' ); ?></h3>
64
+ <form method="post" class="as3cf-create-bucket-form">
65
+ <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
66
+ <input type="text" name="bucket_name" placeholder="<?php _e( 'Bucket Name', 'as3cf' ); ?>">
67
+ <button type="submit" class="button" data-working="<?php _e( 'Creating...', 'as3cf' ); ?>"><?php _e( 'Create', 'as3cf' ); ?></button>
68
+ </form>
69
+ </div>
 
70
 
71
+ <div class="as3cf-main-settings">
72
+ <form method="post">
73
+ <input type="hidden" name="action" value="save" />
74
+ <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
75
 
76
+ <table class="form-table">
77
+ <tr class="as3cf-border-bottom">
78
+ <td><h3><?php _e( 'Bucket', 'as3cf' ); ?></h3></td>
79
+ <td>
80
+ <span class="as3cf-active-bucket"><?php echo $selected_bucket; // xss ok ?></span>
81
+ <a href="#" class="as3cf-change-bucket"><?php _e( 'Change', 'as3cf' ); ?></a>
82
+ <input id="as3cf-bucket" type="hidden" name="bucket" value="<?php echo esc_attr( $selected_bucket ); ?>">
83
+ <input id="as3cf-region" type="hidden" name="region" value="<?php echo esc_attr( $this->get_setting( 'region' ) ); ?>">
84
+ </td>
85
+ </tr>
86
+ <tr>
87
+ <td colspan="2"><h3><?php _e( 'Enable/Disable the Plugin', 'as3cf' ); ?></h3></td>
88
+ </tr>
89
+ <tr>
90
+ <td>
91
+ <?php $this->render_view( 'checkbox', array( 'key' => 'copy-to-s3' ) ); ?>
92
+ </td>
93
+ <td>
94
+ <h4><?php _e( 'Copy Files to S3', 'as3cf' ) ?></h4>
95
+ <p><?php _e( 'When a file is uploaded to the Media Library, copy it to S3. Existing files are <em>not</em> copied to S3.', 'as3cf' ) ?></p>
96
+ </td>
97
+ </tr>
98
+ <tr class="as3cf-border-bottom">
99
+ <td>
100
+ <?php $this->render_view( 'checkbox', array( 'key' => 'serve-from-s3' ) ); ?>
101
+ </td>
102
+ <td>
103
+ <h4><?php _e( 'Rewrite File URLs', 'as3cf' ) ?></h4>
104
+ <p><?php _e( 'For Media Library files that have been copied to S3, rewrite the URLs so that they are served from S3/CloudFront instead of your server.', 'as3cf' ) ?></p>
105
+ </td>
106
+ </tr>
107
+ <tr class="configure-url">
108
+ <td colspan="2"><h3><?php _e( 'Configure File URLs', 'as3cf' ); ?></h3></td>
109
+ </tr>
110
+ <tr class="configure-url">
111
+ <td colspan="2">
112
+ <div class="as3cf-url-preview-wrap">
113
+ <span>Preview</span>
114
+ <div class="as3cf-url-preview">
115
+ <?php echo $this->get_url_preview(); // xss ok ?>
116
+ </div>
117
+ </div>
118
+ </td>
119
+ </tr>
120
+ <tr class="configure-url url-preview">
121
+ <td>
122
+ <h4><?php _e( 'Domain:', 'as3cf' ) ?></h4>
123
+ </td>
124
+ <td>
125
+ <?php
126
+ $domain = $this->get_setting( 'domain' );
127
+ $subdomain_disabled = '';
128
+ $subdomain_class = '';
129
+ if ( 'https' == $this->get_setting( 'ssl' ) ) {
130
+ if ( 'subdomain' == $domain ) {
131
+ $domain = 'path';
132
+ }
133
+ $subdomain_disabled = 'disabled="disabled"';
134
+ $subdomain_class = 'disabled';
135
+ }
136
+ ?>
137
+ <div class="as3cf-domain as3cf-radio-group">
138
+ <label class="subdomain-wrap <?php echo $subdomain_class; // xss ok?>">
139
+ <input type="radio" name="domain" value="subdomain" <?php checked( $domain, 'subdomain' ); ?> <?php echo $subdomain_disabled; // xss ok ?>>
140
+ Bucket name as subdomain
141
+ <p>http://bucket-name.s3.amazon.com/&hellip;</p>
142
+ </label>
143
+ <label>
144
+ <input type="radio" name="domain" value="path" <?php checked( $domain, 'path' ); ?>>
145
+ Bucket name in path
146
+ <p>http://s3.amazon.com/bucket-name/&hellip;</p>
147
+ </label>
148
+ <label>
149
+ <input type="radio" name="domain" value="virtual-host" <?php checked( $domain, 'virtual-host' ); ?>>
150
+ Bucket name as domain
151
+ <p>http://bucket-name/&hellip;</p>
152
+ </label>
153
+ <label>
154
+ <input id="cloudfront" type="radio" name="domain" value="cloudfront" <?php checked( $domain, 'cloudfront' ); ?>>
155
+ CloudFront or custom domain
156
+ <p class="as3cf-setting cloudfront <?php echo ( 'cloudfront' == $domain ) ? '' : 'hide'; // xss ok ?>">
157
+ <input type="text" name="cloudfront" value="<?php echo esc_attr( $this->get_setting( 'cloudfront' ) ); ?>" size="40" />
158
+ </p>
159
+ </label>
160
+ </div>
161
+ </td>
162
+ </tr>
163
+ <tr class="configure-url url-preview">
164
+ <td>
165
+ <?php $this->render_view( 'checkbox', array( 'key' => 'enable-object-prefix', 'class' => 'sub-toggle' ) ); ?>
166
+ </td>
167
+ <td>
168
+ <h4><?php _e( 'Path', 'as3cf' ) ?></h4>
169
+ <p class="object-prefix-desc">
170
+ <?php _e( 'By default the path is the same as your local WordPress files:' ); ?>
171
+ <em><?php echo $this->get_default_object_prefix(); // xss ok ?></em>
172
+ </p>
173
+ <p class="as3cf-setting enable-object-prefix <?php echo ( $this->get_setting( 'enable-object-prefix' ) ) ? '' : 'hide'; // xss ok ?>">
174
+ <input type="text" name="object-prefix" value="<?php echo esc_attr( $this->get_setting( 'object-prefix' ) ); ?>" size="30" />
175
+ </p>
176
+ </td>
177
+ </tr>
178
+ <tr class="configure-url url-preview">
179
+ <td>
180
+ <?php $this->render_view( 'checkbox', array( 'key' => 'use-yearmonth-folders' ) ); ?>
181
+ </td>
182
+ <td>
183
+ <h4><?php _e( 'Year/Month', 'as3cf' ) ?></h4>
184
+ <p>
185
+ <?php _e( 'Add the Year/Month in the URL.' ); ?>
186
+ </p>
187
+ </td>
188
+ </tr>
189
+ <tr class="configure-url as3cf-border-bottom url-preview">
190
+ <td>
191
+ <h4><?php _e( 'SSL', 'as3cf' ) ?></h4>
192
+ </td>
193
+ <td>
194
+ <?php
195
+ $ssl = $this->get_setting( 'ssl' ); ?>
196
+ <div class="as3cf-ssl as3cf-radio-group">
197
+ <label>
198
+ <input type="radio" name="ssl" value="request" <?php checked( $ssl, 'request' ); ?>>
199
+ <?php _e( 'Same as request', 'as3cf' ); ?>
200
+ <p><?php _e( 'When the request is https://, use https:// for the file URL as well.', 'as3cf' ); ?></p>
201
+ </label>
202
+ <label>
203
+ <input type="radio" name="ssl" value="https" <?php checked( $ssl, 'https' ); ?>>
204
+ <?php _e( 'Always SSL', 'as3cf' ); ?>
205
+ <p><?php _e( 'Forces https:// to be used.', 'as3cf' ); ?></p>
206
+ <p><?php _e( 'You cannot use the "Bucket as a subdomain" domain option when using SSL.', 'as3cf' ); ?></p>
207
+ </label>
208
+ <label>
209
+ <input type="radio" name="ssl" value="http" <?php checked( $ssl, 'http' ); ?>>
210
+ <?php _e( 'Always non-SSL', 'as3cf' ); ?>
211
+ <p><?php _e( 'Forces http:// to be used.', 'as3cf' ); ?></p>
212
+ </label>
213
+ </div>
214
+ </td>
215
+ </tr>
216
+ <tr class="advanced-options">
217
+ <td colspan="2"><h3><?php _e( 'Advanced Options', 'as3cf' ); ?></h3></td>
218
+ </tr>
219
+ <tr class="advanced-options">
220
+ <td>
221
+ <?php $this->render_view( 'checkbox', array( 'key' => 'remove-local-file' ) ); ?>
222
+ </td>
223
+ <td>
224
+ <h4><?php _e( 'Remove Files From Server', 'as3cf' ) ?></h4>
225
+ <p><?php _e( 'Once a file has been copied to S3, remove it from the local server.', 'as3cf' ) ?></p>
226
+ </td>
227
+ </tr>
228
+ <tr class="advanced-options url-preview">
229
+ <td>
230
+ <?php $this->render_view( 'checkbox', array( 'key' => 'object-versioning' ) ); ?>
231
+ </td>
232
+ <td>
233
+ <h4><?php _e( 'Object Versioning', 'as3cf' ) ?></h4>
234
+ <p>
235
+ <?php _e( 'Append a timestamp to the S3 file path. Recommended when using CloudFront so you don\'t have to worry about cache invalidation.' ); ?>
236
+ <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ReplacingObjects.html">
237
+ <?php _e( 'More info', 'as3cf' ) ?> &raquo;
238
+ </a>
239
+ </p>
240
+ </td>
241
+ </tr>
242
+ <tr class="advanced-options">
243
+ <td>
244
+ <?php $this->render_view( 'checkbox', array( 'key' => 'expires' ) ); ?>
245
+ </td>
246
+ <td>
247
+ <h4><?php _e( 'Far Future Expiration Header', 'as3cf' ) ?></h4>
248
+ <p><?php _e( 'Implements a "Never Expire" caching policy for browsers by setting an Expires header for 10 years in the future. Should be used in conjunction with object versioning above.' ); ?>
249
+ <a href="http://developer.yahoo.com/performance/rules.html#expires">
250
+ <?php _e( 'More info', 'as3cf' ) ?> &raquo;
251
+ </a>
252
+ </p>
253
+ </td>
254
+ </tr>
255
+ <tr class="advanced-options as3cf-border-bottom">
256
+ <td>
257
+ <?php $this->render_view( 'checkbox', array( 'key' => 'hidpi-images' ) ); ?>
258
+ </td>
259
+ <td>
260
+ <h4><?php _e( 'Copy HiDPI (@2x) Images', 'as3cf' ) ?></h4>
261
+ <p> <?php printf( __( 'When uploading a file to S3, checks if there\'s a file of the same name with an @2x suffix and copies it to S3 as well. Works with the <a href="%s">WP Retina 2x</a> plugin.', 'as3cf' ), 'https://wordpress.org/plugins/wp-retina-2x/' ); ?></p>
262
+ </td>
263
+ </tr>
264
 
265
+ </table>
266
+ <p>
267
+ <button type="submit" class="button button-primary"><?php _e( 'Save Changes', 'amazon-web-services' ); ?></button>
268
+ </p>
269
+ </form>
270
+ </div>
271
 
272
+ <?php $this->render_view( 'sidebar' ); ?>
273
 
274
  </div>
view/sidebar.php CHANGED
@@ -1,6 +1,6 @@
1
  <div class="as3cf-sidebar">
2
 
3
- <div class="as3cf-banner"><img src="<?php echo plugins_url( 'assets/img/snail.jpg', $this->plugin_file_path ); ?>" width="292" height="165" alt="" /></div>
4
 
5
  <form method="post" action="https://deliciousbrains.createsend.com/t/t/s/dlihik/" target="_blank" class="subscribe block">
6
  <h2><?php _e( 'Pro Version?', 'as3cf' ); ?></h2>
@@ -8,18 +8,18 @@
8
  <?php $user = wp_get_current_user(); ?>
9
 
10
  <p class="intro">
11
- <?php echo wptexturize( __( "We're working on a pro version that will include the following features:", 'as3cf' ) ); ?>
12
  </p>
13
 
14
  <ul>
15
- <li><?php echo wptexturize( __( 'Copy existing Media Library to S3', 'as3cf' ) ); ?></li>
16
- <li><?php echo wptexturize( __( 'Serve theme JS & CSS from S3/CloudFront', 'as3cf' ) ); ?></li>
17
- <li><?php echo wptexturize( __( 'WooCommerce & <abbr title="Easy Digital Downloads">EDD</abbr> integration', 'as3cf' ) ); ?></li>
18
- <li><?php echo wptexturize( __( 'Awesome email support', 'as3cf' ) ); ?></li>
19
  </ul>
20
 
21
  <div class="field notify-name">
22
- <input type="text" name="cm-name" value="<?php echo trim( esc_attr( $user->first_name ) . ' ' . esc_attr( $user->last_name ) ); ?>" placeholder="<?php _e( 'Your Name', 'as3cf' ); ?>"/>
23
  </div>
24
 
25
  <div class="field notify-email">
1
  <div class="as3cf-sidebar">
2
 
3
+ <div class="as3cf-banner"><img src="<?php echo esc_url( plugins_url( 'assets/img/snail.jpg', $this->plugin_file_path ) ); ?>" width="292" height="165" alt="" /></div>
4
 
5
  <form method="post" action="https://deliciousbrains.createsend.com/t/t/s/dlihik/" target="_blank" class="subscribe block">
6
  <h2><?php _e( 'Pro Version?', 'as3cf' ); ?></h2>
8
  <?php $user = wp_get_current_user(); ?>
9
 
10
  <p class="intro">
11
+ <?php echo esc_html( wptexturize( __( "We're working on a pro version that will include the following features:", 'as3cf' ) ) ); ?>
12
  </p>
13
 
14
  <ul>
15
+ <li><?php echo esc_html( wptexturize( __( 'Copy existing Media Library to S3', 'as3cf' ) ) ); ?></li>
16
+ <li><?php echo esc_html( wptexturize( __( 'Serve theme JS & CSS from S3/CloudFront', 'as3cf' ) ) ); ?></li>
17
+ <li><?php echo esc_html( wptexturize( __( 'WooCommerce & <abbr title="Easy Digital Downloads">EDD</abbr> integration', 'as3cf' ) ) ); ?></li>
18
+ <li><?php echo esc_html( wptexturize( __( 'Awesome email support', 'as3cf' ) ) ); ?></li>
19
  </ul>
20
 
21
  <div class="field notify-name">
22
+ <input type="text" name="cm-name" value="<?php echo esc_attr( trim( $user->first_name . ' ' . $user->last_name ) ); ?>" placeholder="<?php _e( 'Your Name', 'as3cf' ); ?>"/>
23
  </div>
24
 
25
  <div class="field notify-email">
wordpress-s3.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Amazon S3 and CloudFront
4
  Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/
5
  Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery.
6
  Author: Brad Touesnard
7
- Version: 0.7.2
8
  Author URI: http://bradt.ca
9
  Network: True
10
  Text Domain: as3cf
@@ -26,13 +26,13 @@ Domain Path: /languages/
26
  // Then completely rewritten.
27
  */
28
 
29
- $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '0.7.2';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
  'amazon-s3-and-cloudfront-edd' => '1.0',
33
  );
34
 
35
- $aws_plugin_version_required = '0.2';
36
 
37
  require dirname( __FILE__ ) . '/classes/as3cf-compatibility-check.php';
38
  global $as3cf_compat_check;
4
  Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/
5
  Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery.
6
  Author: Brad Touesnard
7
+ Version: 0.8
8
  Author URI: http://bradt.ca
9
  Network: True
10
  Text Domain: as3cf
26
  // Then completely rewritten.
27
  */
28
 
29
+ $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '0.8';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
  'amazon-s3-and-cloudfront-edd' => '1.0',
33
  );
34
 
35
+ $aws_plugin_version_required = '0.2.1';
36
 
37
  require dirname( __FILE__ ) . '/classes/as3cf-compatibility-check.php';
38
  global $as3cf_compat_check;