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
+ *