WP Offload S3 Lite - Version 0.9

Version Description

  • 2015-07-08 =
  • New: Plugin rebranded to WP Offload S3
  • New: Support tab added to Offload S3 screen containing diagnostic information
  • New: Compatibility with the Media Replace plugin
  • New: Select bucket region when creating a new bucket
  • New: Toggle switches redesigned
  • Improvement: Compatibility with release candidate of Pro plugin
  • Improvement: Example IAM policy more secure
  • Improvement: Set default bucket region using the AS3CF_REGION constant
  • Improvement: Added as3cf_object_meta filter for developers
  • Improvement: Bucket selection moved to modal window
  • Improvement: Don't allow bucket names to contain invalid characters on creation
  • Improvement: More verbose error messages on bucket selection
  • Improvement: Settings link added to plugin row on Plugins screen
  • Improvement: Object versioning enabled by default
  • Improvement: Uninstall routines added
  • Improvement: JavaScript coding standards
  • Improvement: Cache result when checking S3 bucket permissions
  • Bug fix: Bucket region errors result in blank WP Offload S3 screen
  • Bug fix: Editing an image when Remove Files From Server option is enabled results in error
  • Bug fix: Metadata upgrade procedure triggered on new installs
  • Bug fix: File URLs when uploaded to a subdirectory result in incorrect S3 URLs
  • Bug fix: Errors logged when trying to delete non-existent HiDPI images
  • Bug fix: SignatureDoesNotMatch errors on regions with v4 authentication
  • Bug fix: Customizer background image not editable
  • Bug fix: Error when creating buckets with US Standard region
  • Bug fix: Notices appearing incorrectly on some admin screens
  • Bug fix: Subsite upload paths repeated on multisite installs
  • Bug fix: Handle multisite installs where BLOG_ID_CURRENT_SITE is not 1
Download this release

Release Info

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

Code changes from version 0.8.2 to 0.9

README.md ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # WP Offload S3 #
2
+ **Contributors:** bradt
3
+ **Donate link:** https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5VPMGLLK94XJC
4
+ **Tags:** uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
+ **Requires at least:** 3.5
6
+ **Tested up to:** 4.2.2
7
+ **Stable tag:** 0.9
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.
11
+
12
+ ## Description ##
13
+
14
+ This plugin automatically copies images, videos, documents, and any other media added through WordPress' media uploader to [Amazon Simple Storage Service](http://aws.amazon.com/s3/) (S3). It then automatically replaces the URL to each media file with their respective S3 URL or, if you have configured [Amazon CloudFront](http://aws.amazon.com/cloudfront/), the respective CloudFront URL. Image thumbnails are also copied to S3 and delivered through S3/CloudFront.
15
+
16
+ Uploading files *directly* to your S3 account is not currently supported by this plugin. They are uploaded to your server first, then copied to S3. There is an option to automatically remove the files from your server once they are copied to S3 however.
17
+
18
+ If you're adding this plugin to a site that's been around for a while, your existing media files will not be copied or served from S3. Only newly uploaded files will be copied and served from S3.
19
+
20
+ **Pro Version**
21
+
22
+ We’re working on a pro version that will include the following features:
23
+
24
+ * Copy existing Media Library to S3
25
+ * Serve theme JS & CSS from S3/CloudFront
26
+ * WooCommerce & EDD integration
27
+ * Awesome email support
28
+
29
+ [Sign up for news about the pro version](https://confirmsubscription.com/h/t/295CA85AEB94E879)
30
+
31
+ [Request features, report bugs, and submit pull requests on Github](https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/issues)
32
+
33
+ *This plugin has been completely rewritten, but was originally a fork of
34
+ [Amazon S3 for WordPress with CloudFront](http://wordpress.org/extend/plugins/tantan-s3-cloudfront/)
35
+ which is a fork of [Amazon S3 for WordPress](http://wordpress.org/extend/plugins/tantan-s3/), also known as tantan-s3.*
36
+
37
+ ## Installation ##
38
+
39
+ 1. Install the required [Amazon Web Services plugin](http://wordpress.org/extend/plugins/amazon-web-services/) using WordPress' built-in installer
40
+ 2. Follow the instructions to setup your AWS access keys
41
+ 3. Install this plugin using WordPress' built-in installer
42
+ 4. Access the *S3 and CloudFront* option under *AWS* and configure
43
+
44
+ ## Screenshots ##
45
+
46
+ ### 1. Choosing/creating a bucket ###
47
+ ![Choosing/creating a bucket](https://raw.githubusercontent.com/deliciousbrains/wp-wp-offload-s3/assets/screenshot-1.png)
48
+
49
+ ### 2. Settings screen ###
50
+ ![Settings screen](https://raw.githubusercontent.com/deliciousbrains/wp-wp-offload-s3/assets/screenshot-2.png)
51
+
52
+
53
+ ## Upgrade Notice ##
54
+
55
+ ### 0.6 ###
56
+ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
57
+
58
+ ### 0.6.1 ###
59
+ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
60
+
61
+ ### 0.6.2 ###
62
+ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
63
+
64
+ ## Changelog ##
65
+
66
+ ### 0.9 - 2015-07-08 ###
67
+ * New: Plugin rebranded to WP Offload S3
68
+ * New: Support tab added to _Offload S3_ screen containing diagnostic information
69
+ * New: Compatibility with the [Media Replace](https://wordpress.org/plugins/enable-media-replace/) plugin
70
+ * New: Select bucket region when creating a new bucket
71
+ * New: Toggle switches redesigned
72
+ * Improvement: Compatibility with release candidate of Pro plugin
73
+ * Improvement: Example IAM policy more secure
74
+ * Improvement: Set default bucket region using the `AS3CF_REGION` constant
75
+ * Improvement: Added `as3cf_object_meta` filter for developers
76
+ * Improvement: Bucket selection moved to modal window
77
+ * Improvement: Don't allow bucket names to contain invalid characters on creation
78
+ * Improvement: More verbose error messages on bucket selection
79
+ * Improvement: Settings link added to plugin row on _Plugins_ screen
80
+ * Improvement: Object versioning enabled by default
81
+ * Improvement: Uninstall routines added
82
+ * Improvement: JavaScript coding standards
83
+ * Improvement: Cache result when checking S3 bucket permissions
84
+ * Bug fix: Bucket region errors result in blank WP Offload S3 screen
85
+ * Bug fix: Editing an image when _Remove Files From Server_ option is enabled results in error
86
+ * Bug fix: Metadata upgrade procedure triggered on new installs
87
+ * Bug fix: File URLs when uploaded to a subdirectory result in incorrect S3 URLs
88
+ * Bug fix: Errors logged when trying to delete non-existent HiDPI images
89
+ * Bug fix: SignatureDoesNotMatch errors on regions with v4 authentication
90
+ * Bug fix: Customizer background image not editable
91
+ * Bug fix: Error when creating buckets with US Standard region
92
+ * Bug fix: Notices appearing incorrectly on some admin screens
93
+ * Bug fix: Subsite upload paths repeated on multisite installs
94
+ * Bug fix: Handle multisite installs where `BLOG_ID_CURRENT_SITE` is not 1
95
+
96
+ ### 0.8.2 - 2015-01-31 ###
97
+ * New: Input bucket in settings to avoid listing all buckets
98
+ * New: Specify bucket with 'AS3CF_BUCKET' constant
99
+ * Improvement: Compatibility with beta release of Pro plugin
100
+ * Bug Fix: Incorrect file prefix in S3 permission check
101
+
102
+ ### 0.8.1 - 2015-01-19 ###
103
+ * Bug Fix: Permission problems on installs running on EC2s
104
+ * Bug Fix: Blank settings page due to WP_Error on S3 permission check
105
+ * Bug Fix: Warning: strtolower() expects parameter 1 to be string, object given
106
+ * Bug Fix: Region post meta update running on subsites of Multisite installs
107
+
108
+ ### 0.8 - 2015-01-10 ###
109
+ * New: Redesigned settings UI
110
+ * Improvement: SSL setting can be fully controlled, HTTPS for urls always, based on request or never
111
+ * Improvement: Download files from S3 that are not found on server when running Regenerate Thumbnails plugin
112
+ * Improvement: When calling `get_attached_file()` and file is missing from server, return S3 URL
113
+ * Improvement: Code cleanup to WordPress coding standards
114
+ * Bug Fix: Files for all subsites going into the same S3 folder on multisite installs setup prior to WP 3.5
115
+ * Bug Fix: 'attempting to access local file system' error for some installs
116
+
117
+ ### 0.7.2 - 2014-12-11 ###
118
+ * Bug: Some buckets in the EU region causing permission and HTTP errors
119
+ * Bug: Undefined variable: message in view/error.php also causing white screens
120
+
121
+ ### 0.7.1 - 2014-12-05 ###
122
+ * Bug: Read-only error on settings page sometimes false positive
123
+
124
+ ### 0.7 - 2014-12-04 ###
125
+ * New: Proper S3 region subdomain in URLs for buckets not in the US Standard region (e.g. https://s3-us-west-2.amazonaws.com/...)
126
+ * New: Update all existing attachment meta with bucket region (automatically runs in the background)
127
+ * New: Get secure URL for different image sizes (iamzozo)
128
+ * New: S3 bucket can be set with constant in wp-config.php (dberube)
129
+ * New: Filter for allowing/disallowing file types: `as3cf_allowed_mime_types`
130
+ * New: Filter to cancel upload to S3 for any reason: `as3cf_pre_update_attachment_metadata`
131
+ * New: Sidebar with email opt-in
132
+ * Improvement: Show warning when S3 policy is read-only
133
+ * Improvement: Tooltip added to clarify option
134
+ * Improvement: Move object versioning option to make it clear it does not require CloudFront
135
+ * Improvement: By default only allow file types in `get_allowed_mime_types()` to be uploaded to S3
136
+ * Improvement: Compatibility with WPML Media plugin
137
+ * Bug Fix: Edited images not removed on S3 when restoring image and IMAGE_EDIT_OVERWRITE true
138
+ * Bug Fix: File names with certain characters broken not working
139
+ * Bug Fix: Edited image uploaded to incorrect month folder
140
+ * Bug Fix: When creating a new bucket the bucket select box appears empty on success
141
+ * Bug Fix: SSL not working in regions other than US Standard
142
+ * Bug Fix: 'Error uploading' and 'Error removing local file' messages when editing an image
143
+ * Bug Fix: Upload and delete failing when bucket is non-US-region and bucket name contains dot
144
+ * Bug Fix: S3 file overwritten when file with same name uploaded and local file removed (dataferret)
145
+ * Bug Fix: Manually resized images not uploaded (gmauricio)
146
+
147
+ ### 0.6.1 - 2013-09-21 ###
148
+ * WP.org download of Amazon Web Services plugin is giving a 404 Not Found, so directing people to download from Github instead
149
+
150
+ ### 0.6 - 2013-09-20 ###
151
+ * Complete rewrite
152
+ * Now requires PHP 5.3.3+
153
+ * Now requires the [Amazon Web Services plugin](http://wordpress.org/extend/plugins/amazon-web-services/) which contains the latest PHP libraries from Amazon
154
+ * Now works with multisite
155
+ * New Option: Custom S3 object path
156
+ * New Option: Always serve files over https (SSL)
157
+ * New Option: Enable object versioning by appending a timestamp to the S3 file path
158
+ * New Option: Remove uploaded file from local filesystem once it has been copied to S3
159
+ * New Option: Copy any HiDPI (@2x) images to S3 (works with WP Retina 2x plugin)
160
+
161
+ ### 0.5 - 2013-01-29 ###
162
+ * Forked [Amazon S3 for WordPress with CloudFront](http://wordpress.org/extend/plugins/tantan-s3-cloudfront/)
163
+ * Cleaned up the UI to fit with today's WP UI
164
+ * Fixed issues causing error messages when WP_DEBUG is on
165
+ * [Delete files on S3 when deleting WP attachment](https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/commit/e777cd49a4b6999f999bd969241fb24cbbcece60)
166
+ * [Added filter to the get_attachment_url function](https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/commit/bbe1aed5c2ae900e9ba1b16ba6806c28ab8e2f1c)
167
+ * [Added function to get a temporary, secure download URL for private files](https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/commit/11f46ec2714d34907009e37ad3b97f4421aefed3)
assets/.jshintrc DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "boss": true,
3
- "browser": true,
4
- "curly": true,
5
- "devel": true,
6
- "eqeqeq": true,
7
- "eqnull": true,
8
- "es3": false,
9
- "immed": true,
10
- "jquery": true,
11
- "latedef": true,
12
- "maxerr": 10000,
13
- "newcap": true,
14
- "noarg": true,
15
- "sub": true,
16
- "undef": true,
17
- "globals": {
18
- "ajaxurl": true,
19
- "as3cf_i18n": true
20
- }
21
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/Gruntfile.js DELETED
@@ -1,47 +0,0 @@
1
- module.exports = function(grunt) {
2
-
3
- grunt.initConfig({
4
- pkg: grunt.file.readJSON('package.json'),
5
- jshint: {
6
- all: [
7
- 'js/*.js',
8
- '!js/*.min.js'
9
- ],
10
- options: {
11
- jshintrc: '.jshintrc',
12
- force: true
13
- }
14
- },
15
- uglify: {
16
- build: {
17
- files: {
18
- 'js/script.min.js': 'js/script.js'
19
- }
20
- }
21
- },
22
- compass: {
23
- dist: {
24
- options: {
25
- }
26
- }
27
- },
28
- watch: {
29
- js: {
30
- files: ['js/*.js', '!js/*.min.js'],
31
- tasks: ['uglify']
32
- },
33
- sass: {
34
- files: ['sass/*'],
35
- tasks: ['compass']
36
- }
37
- }
38
- });
39
-
40
- grunt.loadNpmTasks('grunt-contrib-jshint');
41
- grunt.loadNpmTasks('grunt-contrib-uglify');
42
- grunt.loadNpmTasks('grunt-contrib-watch');
43
- grunt.loadNpmTasks('grunt-contrib-compass');
44
-
45
- grunt.registerTask('default', ['jshint','uglify','compass']);
46
-
47
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/config.rb DELETED
@@ -1,25 +0,0 @@
1
- # Require any additional compass plugins here.
2
-
3
- # Set this to the root of your project when deployed:
4
- http_path = "/"
5
- css_dir = "css"
6
- sass_dir = "sass"
7
- images_dir = "img"
8
- javascripts_dir = "js"
9
-
10
- # You can select your preferred output style here (can be overridden via the command line):
11
- # output_style = :expanded or :nested or :compact or :compressed
12
- output_style = :compressed
13
-
14
- # To enable relative paths to assets via compass helper functions. Uncomment:
15
- # relative_assets = true
16
-
17
- # To disable debugging comments that display the original location of your selectors. Uncomment:
18
- line_comments = false
19
-
20
-
21
- # If you prefer the indented syntax, you might want to regenerate this
22
- # project again passing --syntax sass, or you can uncomment this:
23
- # preferred_syntax = :sass
24
- # and then run:
25
- # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/modal.css ADDED
@@ -0,0 +1 @@
 
1
+ #as3cf-overlay{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,0.5);overflow:hidden;overflow-y:auto;z-index:999999}#as3cf-modal{display:none;position:relative;width:600px;margin:100px auto;padding:30px;background-color:#eee;box-shadow:0 0 10px rgba(0,0,0,0.5);font-size:14px;overflow:hidden;z-index:100000}#as3cf-modal .close-as3cf-modal{color:#999;cursor:pointer;font-family:"Times New Roman", serif;font-size:26px;font-weight:200;position:absolute;right:18px;top:18px}#as3cf-modal .close-as3cf-modal:hover{color:#666}#as3cf-modal h3{margin:0 0 20px;font-weight:normal;line-height:1}#as3cf-modal .error,#as3cf-modal .notice,#as3cf-modal .updated{margin:0 0 20px}#as3cf-modal .actions{margin:20px -30px -30px;padding:20px 30px;border-top:none;background-color:#e3e3e3;overflow:hidden}#as3cf-modal .actions .right{margin-left:15px}#as3cf-modal .actions .right:last-of-type{margin-left:0}#as3cf-modal .actions button{min-width:90px}body.as3cf-modal-open{overflow:hidden;padding-right:15px}
assets/css/styles.css CHANGED
@@ -1 +1 @@
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.as3cf-notice,.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 .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-select.manual .as3cf-manual-bucket-toggle,.as3cf-settings .as3cf-bucket-select.manual .as3cf-refresh-buckets,.as3cf-settings .as3cf-bucket-select.manual .as3cf-bucket-list-wrapper{display:none}.as3cf-settings .as3cf-bucket-select.manual .as3cf-bucket-list-toggle,.as3cf-settings .as3cf-bucket-select.manual .as3cf-manual-save-bucket-wrapper{display:block}.as3cf-settings .as3cf-bucket-select.manual .as3cf-cancel-bucket-select-wrap:after{content:""}.as3cf-settings .as3cf-bucket-select .as3cf-manual-bucket-toggle,.as3cf-settings .as3cf-bucket-select .as3cf-bucket-list-wrapper{display:block}.as3cf-settings .as3cf-bucket-select .as3cf-refresh-buckets{display:inline}.as3cf-settings .as3cf-bucket-select .as3cf-bucket-list-toggle,.as3cf-settings .as3cf-bucket-select .as3cf-manual-save-bucket-wrapper{display:none}.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 input[type="text"].as3cf-bucket-name{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}}
1
+ .aws-main.wrap>h2{float:left}.aws-main.wrap .as3cf-notice,.aws-main.wrap .as3cf-updated,.aws-main.wrap .as3cf-error{margin-bottom:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.aws-main.wrap .as3cf-error.fatal{clear:both;float:left}.aws-main.wrap h2.nav-tab-wrapper{float:none;margin-bottom:0;width:650px;margin-top:10px;padding-left:5px;padding-right:0}.aws-main.wrap h2.nav-tab-wrapper a.nav-tab-active{color:#464646;cursor:default}.aws-main.wrap h2.nav-tab-wrapper a:focus{-webkit-box-shadow:none;box-shadow:none}.aws-main.wrap .error pre{background:#eaeaea;background:rgba(0,0,0,0.07);display:block;padding:10px 15px}.aws-main.wrap .error pre code{padding:0;background:none}.aws-main.wrap[data-tab="support"] .as3cf-notice,.aws-main.wrap[data-tab="support"] .error,.aws-main.wrap[data-tab="support"] .updated,.aws-main.wrap[data-tab="support"] .updated.show{display:none}.aws-main.wrap[data-tab="support"] .fatal .error,.aws-main.wrap[data-tab="support"] .dbrains-api-down{display:block}.aws-main.wrap .as3cf-notice,.aws-main.wrap .error,.aws-main.wrap .updated{max-width:650px;margin-top:15px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.aws-main.wrap .as3cf-updated{display:none}.aws-main.wrap .as3cf-updated.as3cf-notice,.aws-main.wrap .as3cf-updated.show{display:block}.as3cf-tab .as3cf-main-settings{display:none}.as3cf-tab .as3cf-bucket-container{display:block}.as3cf-tab.as3cf-has-bucket .as3cf-main-settings{display:block}.as3cf-tab.as3cf-has-bucket .as3cf-bucket-container{display:none}.as3cf-tab{display:none;position:relative;width:650px}.as3cf-tab .as3cf-main-settings p{font-size:13px}.as3cf-tab .as3cf-main-settings p a{color:#444}.as3cf-tab .object-prefix-desc em{white-space:nowrap}.as3cf-tab .as3cf-url-preview-wrap{background:#fff;text-align:center;padding:20px 0 0;max-width:650px;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.as3cf-tab .as3cf-url-preview-wrap .as3cf-url-preview{margin-top:10px;padding:0 20px 10px;overflow-x:scroll}.as3cf-tab .as3cf-url-preview-wrap span{color:#aaa;text-transform:uppercase;font-weight:bold}.as3cf-tab .as3cf-ssl p.info{margin-top:10px;padding:0}.as3cf-tab .as3cf-radio-group label{display:block;margin-bottom:10px}.as3cf-tab .as3cf-radio-group label.disabled,.as3cf-tab .as3cf-radio-group label.disabled p{color:#bbbbbb;cursor:default}.as3cf-tab .as3cf-radio-group p{padding-left:25px;color:#6b6b6b;margin:0;font-size:12px}.as3cf-tab .as3cf-radio-group p.as3cf-setting{margin-top:5px}.as3cf-tab .as3cf-switch{position:relative;display:inline-block;padding:2px;overflow:hidden;border-radius:2px;-webkit-border-radius:2px;background-color:#d4d3d3;cursor:pointer}.as3cf-tab .as3cf-switch.on{background-color:#ade7b5}.as3cf-tab .as3cf-switch span{visibility:hidden;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;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:#fff;color:#8d8d8d;z-index:1}.as3cf-tab .as3cf-switch span.on{color:#82d78b}.as3cf-tab .as3cf-switch span.checked{visibility:visible}.as3cf-tab .as3cf-switch.disabled{cursor:default;background:#e6e6e6}.as3cf-tab .as3cf-switch.disabled span{background:#f1f1f1;color:#d6d6d6}.as3cf-tab .as3cf-switch input[type="checkbox"]{position:absolute !important;top:0;left:0;opacity:0;filter:alpha(opacity=0);z-index:-1}.as3cf-tab .as3cf-setting.hide{display:none}.as3cf-tab h3{font-weight:normal;text-transform:uppercase;margin:15px 0}.as3cf-tab .form-table{margin:0}.as3cf-tab .form-table tr.as3cf-border-bottom td{border-bottom:1px solid #ddd;padding:20px 0px}.as3cf-tab .form-table tr.as3cf-setting-title td{padding-bottom:0}.as3cf-tab .form-table tr.as3cf-setting-title:first-child td{padding-top:20px}.as3cf-tab .form-table tr td{padding:15px 0}.as3cf-tab .form-table tr td:first-child{vertical-align:top;min-width:120px}.as3cf-tab .form-table h3{padding:0;margin:0}.as3cf-tab .form-table h4{margin:0}.as3cf-tab .as3cf-active-bucket{font-weight:bold;margin-right:10px}.as3cf-tab .tooltip{position:relative;z-index:2;cursor:pointer}.as3cf-tab .tooltip:before,.as3cf-tab .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-tab .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-tab .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-tab .tooltip:hover:before,.as3cf-tab .tooltip:hover:after{visibility:visible;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);opacity:1}#tab-media{display:block}#tab-media .as3cf-main-settings{display:none}#tab-media .as3cf-bucket-container{display:block}#tab-media.as3cf-has-bucket .as3cf-main-settings{display:block}#tab-media.as3cf-has-bucket .as3cf-bucket-container{display:none}.as3cf-bucket-container h3{line-height:1.3;text-transform:none}.as3cf-bucket-container a:focus{-webkit-box-shadow:none;box-shadow:none;outline:none}.as3cf-bucket-container input[type=text]{box-sizing:border-box;width:100%}.as3cf-bucket-container select{box-sizing:border-box;width:50%}.as3cf-bucket-container .form-table td{padding:5px 0}.as3cf-bucket-container .form-table td:first-child{width:100px;line-height:30px;vertical-align:top}.as3cf-bucket-container .as3cf-invalid-bucket-name{font-size:12px;color:#a00}.as3cf-bucket-container .bucket-actions{margin:15px 0;border-top:1px solid #ccc;padding-top:15px;overflow:hidden}.as3cf-bucket-container .bucket-actions button,.as3cf-bucket-container .bucket-actions .right{float:right;margin-right:0}.as3cf-bucket-container .bucket-actions span{display:inline-block;margin-right:20px;line-height:28px}.as3cf-bucket-container .bucket-actions .bucket-action-cancel{color:#a00;text-decoration:none}.as3cf-bucket-container .bucket-actions .bucket-action-cancel:hover{color:red}.as3cf-bucket-container .as3cf-bucket-list{padding:15px;max-height:200px;overflow-x:hidden;overflow-y:auto;background-color:#fff;font-size:14px}.as3cf-bucket-container .as3cf-bucket-list li:last-of-type{margin-bottom:0}.as3cf-bucket-container .as3cf-bucket-list a{color:#444;text-decoration:none}.as3cf-bucket-container .as3cf-bucket-list a:hover{color:#0074A2}.as3cf-bucket-container .as3cf-bucket-list a.selected{font-weight:bold;color:#0074A2}.as3cf-bucket-container .as3cf-bucket-list a .dashicons{margin-right:5px}.as3cf-bucket-container .as3cf-bucket-select,.as3cf-bucket-container .as3cf-bucket-create{display:none}.as3cf-bucket-container .bucket-actions.select{display:none}.as3cf-tab{display:none}#tab-media{display:block}#tab-support .as3cf-sidebar{top:11px}#tab-support .support-section{border-bottom:1px solid #ccc;padding-bottom:20px;margin-bottom:20px}#tab-support .debug textarea{width:100%;min-height:200px;font-family:Consolas, Monaco, monospace;margin-bottom:5px}.as3cf-sidebar{position:absolute;top:25px;left:670px;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}}.as3cf-banner img{display:block}.aws-compatibility-notice.error{clear:both;margin:5px 20px 5px 0}
assets/js/modal.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var as3cfModal = (function ( $ ) {
2
+
3
+ var modal = {
4
+ prefix: 'as3cf'
5
+ };
6
+
7
+ var modals = {};
8
+
9
+ /**
10
+ * Target to key
11
+ *
12
+ * @param string target
13
+ *
14
+ * @return string
15
+ */
16
+ function targetToKey( target ) {
17
+ return target.replace( /[^a-z]/g, '' );
18
+ }
19
+
20
+ /**
21
+ * Open modal
22
+ *
23
+ * @param string target
24
+ * @param function callback
25
+ */
26
+ modal.open = function ( target, callback ) {
27
+ var key = targetToKey( target );
28
+
29
+ // Overlay
30
+ $( 'body' ).append( '<div id="as3cf-overlay"></div>' );
31
+ var $overlay = $( '#as3cf-overlay' );
32
+
33
+ // Modal container
34
+ $overlay.append( '<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>' );
35
+ var $modal = $( '#as3cf-modal' );
36
+
37
+ if ( undefined === modals[ key ] ) {
38
+ var content = $( target );
39
+ modals[ key ] = content.clone( true ).css( 'display', 'block' );
40
+ content.remove();
41
+ }
42
+ $modal.data( 'as3cf-modal-target', target ).append( modals[ key ] );
43
+
44
+ if ( 'function' === typeof callback ) {
45
+ callback( target );
46
+ }
47
+
48
+ // Handle modals taller than window height,
49
+ // overflow & padding-right remove duplicate scrollbars.
50
+ $( 'body' ).addClass( 'as3cf-modal-open' );
51
+
52
+ $overlay.fadeIn( 150 );
53
+ $modal.fadeIn( 150 );
54
+
55
+ $( 'body' ).trigger( 'as3cf-modal-open', [ target ] );
56
+ };
57
+
58
+ /**
59
+ * Close modal
60
+ *
61
+ * @param function callback
62
+ */
63
+ modal.close = function ( callback ) {
64
+ var target = $( '#as3cf-modal' ).data( 'as3cf-modal-target' );
65
+
66
+ $( '#as3cf-overlay' ).fadeOut( 150, function () {
67
+ if ( 'function' === typeof callback ) {
68
+ callback( target );
69
+ }
70
+
71
+ $( 'body' ).removeClass( 'as3cf-modal-open' );
72
+
73
+ $( this ).remove();
74
+ } );
75
+
76
+ $( 'body' ).trigger( 'as3cf-modal-close', [ target ] );
77
+ };
78
+
79
+ // Setup click handlers
80
+ $( document ).ready( function () {
81
+
82
+ $( 'body' ).on( 'click', '[data-as3cf-modal]', function ( e ) {
83
+ e.preventDefault();
84
+ modal.open( $( this ).data( 'as3cf-modal' ) + '.' + modal.prefix );
85
+ } );
86
+
87
+ $( 'body' ).on( 'click', '#as3cf-overlay, .close-as3cf-modal', function ( e ) {
88
+ e.preventDefault();
89
+
90
+ // Don't allow children to bubble up click event
91
+ if ( e.target !== this ) {
92
+ return false;
93
+ }
94
+
95
+ modal.close();
96
+ } );
97
+
98
+ } );
99
+
100
+ return modal;
101
+
102
+ })( jQuery );
103
+
104
+ var as3cfFindAndReplaceModal = (function ( $, as3cfModal ) {
105
+
106
+ var modal = {
107
+ selector: '.as3cf-find-replace-container',
108
+ isBulk: false,
109
+ link: null,
110
+ payload: {}
111
+ };
112
+
113
+ /**
114
+ * Open modal
115
+ *
116
+ * @param string link
117
+ * @param mixed payload
118
+ */
119
+ modal.open = function ( link, payload ) {
120
+ if ( typeof link !== 'undefined' ) {
121
+ modal.link = link;
122
+ }
123
+ if ( typeof payload !== 'undefined' ) {
124
+ modal.payload = payload;
125
+ }
126
+
127
+ as3cfModal.open( modal.selector );
128
+
129
+ $( modal.selector ).find( '.single-file' ).show();
130
+ $( modal.selector ).find( '.multiple-files' ).hide();
131
+ if ( modal.isBulk ) {
132
+ $( modal.selector ).find( '.single-file' ).hide();
133
+ $( modal.selector ).find( '.multiple-files' ).show();
134
+ }
135
+ };
136
+
137
+ /**
138
+ * Close modal
139
+ */
140
+ modal.close = function () {
141
+ as3cfModal.close( modal.selector );
142
+ };
143
+
144
+ /**
145
+ * Set the isBulk flag
146
+ */
147
+ modal.setBulk = function ( isBulk ) {
148
+ modal.isBulk = isBulk;
149
+ };
150
+
151
+ /**
152
+ * Create the loading state
153
+ */
154
+ modal.startLoading = function () {
155
+ $( modal.selector + ' [data-find-replace]' ).prop( 'disabled', true ).siblings( '.spinner' ).css( 'visibility', 'visible' ).show();
156
+ };
157
+
158
+ /**
159
+ * Remove the loading state
160
+ */
161
+ modal.stopLoading = function () {
162
+ $( modal.selector + ' [data-find-replace]' ).prop( 'disabled', false ).siblings( '.spinner' ).css( 'visibility', 'hidden' ).hide();
163
+ };
164
+
165
+ // Setup click handlers
166
+ $( document ).ready( function () {
167
+
168
+ $( 'body' ).on( 'click', modal.selector + ' [data-find-replace]', function ( e ) {
169
+ var findAndReplace = $( this ).data( 'find-replace' );
170
+
171
+ if ( !modal.link ) {
172
+ // If there is no link set then this must be an AJAX
173
+ // request so trigger an event instead
174
+ $( modal.selector ).trigger( 'as3cf-find-and-replace', [ findAndReplace, modal.payload ] );
175
+ return;
176
+ }
177
+
178
+ if ( findAndReplace ) {
179
+ modal.link += '&find_and_replace=1';
180
+ }
181
+
182
+ modal.startLoading();
183
+
184
+ window.location = modal.link;
185
+ } );
186
+
187
+ $( 'body' ).on( 'as3cf-modal-close', function ( e ) {
188
+ modal.isBulk = false;
189
+ modal.link = null;
190
+ modal.payload = {};
191
+ } );
192
+
193
+ } );
194
+
195
+ return modal;
196
+
197
+ })( jQuery, as3cfModal );
198
+
199
+
assets/js/modal.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var as3cfModal=function(a){function b(a){return a.replace(/[^a-z]/g,"")}var c={prefix:"as3cf"},d={};return c.open=function(c,e){var f=b(c);a("body").append('<div id="as3cf-overlay"></div>');var g=a("#as3cf-overlay");g.append('<div id="as3cf-modal"><span class="close-as3cf-modal">×</span></div>');var h=a("#as3cf-modal");if(void 0===d[f]){var i=a(c);d[f]=i.clone(!0).css("display","block"),i.remove()}h.data("as3cf-modal-target",c).append(d[f]),"function"==typeof e&&e(c),a("body").addClass("as3cf-modal-open"),g.fadeIn(150),h.fadeIn(150),a("body").trigger("as3cf-modal-open",[c])},c.close=function(b){var c=a("#as3cf-modal").data("as3cf-modal-target");a("#as3cf-overlay").fadeOut(150,function(){"function"==typeof b&&b(c),a("body").removeClass("as3cf-modal-open"),a(this).remove()}),a("body").trigger("as3cf-modal-close",[c])},a(document).ready(function(){a("body").on("click","[data-as3cf-modal]",function(b){b.preventDefault(),c.open(a(this).data("as3cf-modal")+"."+c.prefix)}),a("body").on("click","#as3cf-overlay, .close-as3cf-modal",function(a){return a.preventDefault(),a.target!==this?!1:void c.close()})}),c}(jQuery),as3cfFindAndReplaceModal=function(a,b){var c={selector:".as3cf-find-replace-container",isBulk:!1,link:null,payload:{}};return c.open=function(d,e){"undefined"!=typeof d&&(c.link=d),"undefined"!=typeof e&&(c.payload=e),b.open(c.selector),a(c.selector).find(".single-file").show(),a(c.selector).find(".multiple-files").hide(),c.isBulk&&(a(c.selector).find(".single-file").hide(),a(c.selector).find(".multiple-files").show())},c.close=function(){b.close(c.selector)},c.setBulk=function(a){c.isBulk=a},c.startLoading=function(){a(c.selector+" [data-find-replace]").prop("disabled",!0).siblings(".spinner").css("visibility","visible").show()},c.stopLoading=function(){a(c.selector+" [data-find-replace]").prop("disabled",!1).siblings(".spinner").css("visibility","hidden").hide()},a(document).ready(function(){a("body").on("click",c.selector+" [data-find-replace]",function(){var b=a(this).data("find-replace");return c.link?(b&&(c.link+="&find_and_replace=1"),c.startLoading(),void(window.location=c.link)):void a(c.selector).trigger("as3cf-find-and-replace",[b,c.payload])}),a("body").on("as3cf-modal-close",function(){c.isBulk=!1,c.link=null,c.payload={}})}),c}(jQuery,as3cfModal);
assets/js/script.js CHANGED
@@ -1,311 +1,560 @@
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
- var $manualBucketForm = $container.find( '.as3cf-manual-save-bucket-form' );
11
 
12
- if ( $createBucketForm.length ) {
13
- var $createBucketButton = $createBucketForm.find( 'button' );
14
- var origButtonText = $createBucketButton.text();
15
 
16
- $createBucketForm.on( 'submit', function( e ) {
17
- e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  $( '.as3cf-bucket-error' ).hide();
19
- $bucketList.addClass( 'saving' );
20
- $createBucketButton.text( $createBucketButton.attr( 'data-working' ) );
21
- $createBucketButton.prop( 'disabled', true );
22
- var bucketName = $createBucketForm.find( 'input[name="bucket_name"]' ).val();
23
-
24
- var data = {
25
- action : 'as3cf-create-bucket',
26
- bucket_name: bucketName,
27
- _nonce : as3cf_i18n.create_bucket_nonce
28
- };
29
-
30
- $.ajax( {
31
- url : ajaxurl,
32
- type : 'POST',
33
- dataType: 'JSON',
34
- data : data,
35
- error : function( jqXHR, textStatus, errorThrown ) {
36
- $createBucketButton.text( origButtonText );
37
- show_bucket_error( as3cf_i18n.create_bucket_error, errorThrown );
38
- },
39
- success : function( data, textStatus, jqXHR ) {
40
- $createBucketButton.text( origButtonText );
41
- $createBucketButton.prop( 'disabled', false );
42
- if ( typeof data[ 'success' ] !== 'undefined' ) {
43
- bucket_select( bucketName, data[ 'region' ], data[ 'can_write' ] );
44
- // tidy up create bucket form
45
- $createBucketForm.find( 'input[name="bucket_name"]' ).val( '' );
46
- if ( $( '.as3cf-bucket-list-wrapper' ).is( ':visible' ) ) {
47
- loadBuckets();
48
- }
49
- $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
50
- $bucketList.removeClass( 'saving' );
51
- $manualBucketForm.find( 'input[name="bucket_name"]' ).val( bucketName );
52
- } else {
53
- show_bucket_error( as3cf_i18n.create_bucket_error, data[ 'error' ] );
54
- }
55
- }
56
- } );
57
- } );
58
- }
59
 
60
- if ( $manualBucketForm.length ) {
61
- var $manualBucketButton = $manualBucketForm.find( 'button' );
62
- var origManualButtonText = $manualBucketButton.text();
63
-
64
- $manualBucketForm.on( 'submit', function( e ) {
65
- e.preventDefault();
66
- var bucketName = $manualBucketForm.find( 'input[name="bucket_name"]' ).val();
67
- if ( bucketName === $( '.as3cf-active-bucket' ).text() ) {
68
- $( '.as3cf-bucket-error' ).hide();
69
- $( '.as3cf-settings' ).addClass( 'as3cf-has-bucket' );
70
- return;
71
- }
72
- $( '.as3cf-bucket-error' ).hide();
73
- $manualBucketButton.text( $manualBucketButton.attr( 'data-working' ) );
74
- $manualBucketButton.prop( 'disabled', true );
75
-
76
- var data = {
77
- action : 'as3cf-manual-save-bucket',
78
- bucket_name: bucketName,
79
- _nonce : as3cf_i18n.manual_bucket_nonce
80
- };
81
-
82
- $.ajax( {
83
- url : ajaxurl,
84
- type : 'POST',
85
- dataType: 'JSON',
86
- data : data,
87
- error : function( jqXHR, textStatus, errorThrown ) {
88
- $manualBucketButton.text( origManualButtonText );
89
- show_bucket_error( as3cf_i18n.save_bucket_error, errorThrown );
90
- },
91
- success : function( data, textStatus, jqXHR ) {
92
- $manualBucketButton.text( origManualButtonText );
93
- $manualBucketButton.prop( 'disabled', false );
94
- if ( typeof data[ 'success' ] !== 'undefined' ) {
95
- bucket_select( bucketName, data[ 'region' ], data[ 'can_write' ] );
96
- $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
97
- } else {
98
- show_bucket_error( as3cf_i18n.save_bucket_error, data[ 'error' ] );
99
- }
100
- }
101
  } );
102
- } );
103
- }
104
-
105
- var $changeBucket = $container.find( '.as3cf-change-bucket' );
106
- if ( $changeBucket.length ) {
107
- $changeBucket.on( 'click', function( e ) {
108
- e.preventDefault();
109
- $( '.updated' ).not( '.as3cf-notice' ).hide();
110
- $( '.as3cf-can-write-error' ).hide();
111
- $( '.as3cf-settings' ).removeClass( 'as3cf-has-bucket' );
112
- if ( $( '.as3cf-bucket-list-wrapper' ).is( ':visible' ) ) {
113
- loadBuckets();
114
- if ( $( '.as3cf-active-bucket' ).html ) {
115
- $( '.as3cf-cancel-bucket-select-wrap' ).show();
116
- }
117
- }
118
- } );
119
- }
120
 
121
- var $refreshBuckets = $container.find( '.as3cf-refresh-buckets' );
122
- if ( $refreshBuckets.length ) {
123
- $refreshBuckets.on( 'click', function( e ) {
124
- e.preventDefault();
125
- loadBuckets();
126
- } );
127
  }
 
 
 
 
 
 
 
 
 
 
128
 
129
- var $cancelChangeBucket = $container.find( '.as3cf-cancel-bucket-select' );
130
- if ( $cancelChangeBucket.length ) {
131
- $cancelChangeBucket.on( 'click', function( e ) {
132
- e.preventDefault();
133
- $( '.as3cf-bucket-error' ).hide();
134
- $( '.as3cf-settings' ).addClass( 'as3cf-has-bucket' );
135
- } );
136
- }
137
 
 
 
138
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- var $bucketList = $( '.as3cf-bucket-list' );
141
 
142
- function loadBuckets() {
143
- $( '.as3cf-bucket-error' ).hide();
144
- $bucketList.html( '<li class="loading">' + $bucketList.attr( 'data-working' ) + '</li>' );
145
-
146
- var data = {
147
- action: 'as3cf-get-buckets',
148
- _nonce: as3cf_i18n.get_buckets_nonce
149
- };
150
-
151
- $.ajax( {
152
- url : ajaxurl,
153
- type : 'POST',
154
- dataType: 'JSON',
155
- data : data,
156
- error : function( jqXHR, textStatus, errorThrown ) {
157
- $bucketList.html( '' );
158
- show_bucket_error( as3cf_i18n.get_buckets_error, errorThrown );
159
- },
160
- success : function( data, textStatus, jqXHR ) {
161
- $bucketList.html( '' );
162
- if ( typeof data[ 'success' ] !== 'undefined' ) {
163
- $( data[ 'buckets' ] ).each( function( idx, bucket ) {
164
- var bucket_class = (
165
- bucket.Name === data[ 'selected' ]
166
- ) ? 'selected' : '';
167
- $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>' );
168
- } );
169
- scroll_to_selected_bucket();
170
- } else {
171
- show_bucket_error( as3cf_i18n.get_buckets_error, data[ 'error' ] );
172
- }
173
- }
174
- } );
175
- }
176
 
177
- $bucketList.on( 'click', 'a', function( e ) {
178
- e.preventDefault();
 
 
 
 
 
 
 
179
 
180
- if ( $( this ).hasClass( 'selected' ) ) {
181
- $( '.as3cf-settings' ).addClass( 'as3cf-has-bucket' );
182
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
 
 
 
 
 
 
 
 
 
 
184
 
185
- var bucket = this;
186
- var previous_bucket = $( '.as3cf-bucket-list a.selected' ).attr( 'data-bucket' );
 
 
 
187
 
188
- $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
189
- $( bucket ).addClass( 'selected' );
190
-
191
- $bucketList.addClass( 'saving' );
192
- $( bucket ).find( '.spinner' ).show();
193
- var bucketName = $( bucket ).attr( 'data-bucket' );
194
-
195
- var data = {
196
- action : 'as3cf-save-bucket',
197
- bucket_name: bucketName,
198
- _nonce : as3cf_i18n.save_bucket_nonce
199
- };
200
-
201
- $.ajax( {
202
- url : ajaxurl,
203
- type : 'POST',
204
- dataType: 'JSON',
205
- data : data,
206
- error : function( jqXHR, textStatus, errorThrown ) {
207
- $bucketList.removeClass( 'saving' );
208
- show_bucket_error( as3cf_i18n.save_bucket_error, errorThrown );
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
210
- $( '.as3cf-bucket-list a[data-bucket="' + previous_bucket + '"]' ).addClass( 'selected' );
211
- },
212
- success : function( data, textStatus, jqXHR ) {
213
- $( bucket ).find( '.spinner' ).hide();
214
- $bucketList.removeClass( 'saving' );
215
- if ( typeof data[ 'success' ] !== 'undefined' ) {
216
- bucket_select( bucketName, data[ 'region' ], data[ 'can_write' ] );
217
- } else {
218
- show_bucket_error( as3cf_i18n.save_bucket_error, data[ 'error' ] );
219
- $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
220
- $( '.as3cf-bucket-list a[data-bucket="' + previous_bucket + '"]' ).addClass( 'selected' );
221
- }
222
  }
223
- } );
224
  } );
 
 
 
 
 
 
 
 
 
225
 
226
- function scroll_to_selected_bucket() {
227
- if ( ! $( '.as3cf-bucket-list a.selected' ).length ) {
228
- return;
229
- }
230
 
231
- var offset = $( 'ul.as3cf-bucket-list li' ).first().position().top + 150;
 
 
 
 
 
232
 
233
- $( 'ul.as3cf-bucket-list' ).animate( {
234
- scrollTop: $( 'ul.as3cf-bucket-list li a.selected' ).position().top - offset
235
- } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
237
 
238
- function show_bucket_error( title, error ) {
239
- $( '.as3cf-bucket-error span.title' ).html( title );
240
- $( '.as3cf-bucket-error span.message' ).html( error );
241
- $( '.as3cf-bucket-error' ).show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  }
243
 
244
- function bucket_select( bucket, region, can_write ) {
245
- if ( '' === $( '.as3cf-active-bucket' ).text() ) {
246
- // first time bucket select - enable main options by default
247
- set_checkbox( 'copy-to-s3-wrap' );
248
- set_checkbox( 'serve-from-s3-wrap' );
249
- }
250
- $( '.as3cf-active-bucket' ).text( bucket );
251
- $( 'form.as3cf-manual-save-bucket-form .as3cf-bucket-name' ).val( bucket );
252
- $( '#as3cf-bucket' ).val( bucket );
253
- $( '#as3cf-region' ).val( region );
254
- $( '.updated' ).not( '.as3cf-notice' ).show();
255
- // check permission on bucket
256
- if ( can_write === false ) {
257
- $( '.as3cf-can-write-error' ).show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
259
- $( '.as3cf-settings' ).addClass( 'as3cf-has-bucket' );
260
- generate_url_preview();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
262
 
263
- $( '.as3cf-switch' ).on( 'click', 'span', function( e ) {
264
- if ( ! $( this ).parent().hasClass( 'disabled' ) ) {
265
- var parent_id = $( this ).parent().attr( 'id' );
266
- set_checkbox( parent_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  }
268
  } );
 
 
 
269
 
270
- function set_checkbox( checkbox_wrap ) {
271
- $( '#' + checkbox_wrap + ' span' ).toggleClass( 'checked' );
272
- var switch_on = $( '#' + checkbox_wrap + ' span.on' ).hasClass( 'checked' );
273
- var checkbox_name = $( '#' + checkbox_wrap ).data( 'checkbox' );
274
- var $checkbox = $( 'input#' + checkbox_name );
275
- $checkbox.attr( "checked", switch_on );
276
- $checkbox.trigger( "change" );
 
 
 
 
 
 
 
 
 
277
  }
278
 
279
- if ( $( '.as3cf-settings' ).length && ! $( '.as3cf-settings' ).hasClass( 'as3cf-has-bucket' ) ) {
280
- if ( $( '.as3cf-bucket-list-wrapper' ).is( ':visible' ) ) {
281
- loadBuckets();
 
282
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
284
 
285
- $( '.as3cf-settings' ).on( 'change', '.sub-toggle', function( e ) {
286
- var setting = $( this ).attr( 'id' );
287
- $( '.as3cf-setting.' + setting ).toggleClass( 'hide' );
 
 
 
 
 
 
 
 
288
  } );
289
 
290
- $( '.as3cf-settings' ).on( 'click', '.as3cf-manual-bucket-toggle', function( e ) {
291
- e.preventDefault();
292
- $( '.as3cf-bucket-select' ).addClass( 'manual' );
 
293
  } );
294
 
295
- $( '.as3cf-settings' ).on( 'click', '.as3cf-bucket-list-toggle', function( e ) {
296
- e.preventDefault();
297
- loadBuckets();
298
- $( '.as3cf-bucket-select' ).removeClass( 'manual' );
299
- $( '.as3cf-cancel-bucket-select-wrap' ).show();
 
 
 
 
300
  } );
301
 
302
  $( '.as3cf-domain' ).on( 'change', 'input[type="radio"]', function( e ) {
303
- var domain = $( 'input:radio[name="domain"]:checked' ).val();
304
- if ( 'cloudfront' === domain && $( '.as3cf-setting.cloudfront' ).hasClass( 'hide' ) ) {
305
- $( '.as3cf-setting.cloudfront' ).removeClass( 'hide' );
306
- } else {
307
- $( '.as3cf-setting.cloudfront' ).addClass( 'hide' );
308
- }
309
  } );
310
 
311
  $( '.as3cf-ssl' ).on( 'change', 'input[type="radio"]', function( e ) {
@@ -324,72 +573,98 @@
324
  } );
325
 
326
  $( '.url-preview' ).on( 'change', 'input', function( e ) {
327
- generate_url_preview();
328
  } );
329
 
330
- function generate_url_preview() {
331
- $( '.as3cf-url-preview' ).html( 'Generating...' );
332
 
333
- var data = {
334
- _nonce: as3cf_i18n.get_url_preview_nonce
335
- };
336
 
337
- $.each( $( ".as3cf-main-settings form" ).serializeArray(), function( i, o ) {
338
- var n = o.name,
339
- v = o.value;
340
- n = n.replace( '[]', '' );
341
- data[ n ] = data[ n ] === undefined ? v
342
- : $.isArray( data[ n ] ) ? data[ n ].concat( v )
343
- : [ data[ n ], v ];
344
- } );
345
 
346
- // overwrite the save action stored in the form
347
- data[ 'action' ] = 'as3cf-get-url-preview';
348
-
349
- $.ajax( {
350
- url : ajaxurl,
351
- type : 'POST',
352
- dataType: 'JSON',
353
- data : data,
354
- error : function( jqXHR, textStatus, errorThrown ) {
355
- alert( as3cf_i18n.get_url_preview_error + errorThrown );
356
- },
357
- success : function( data, textStatus, jqXHR ) {
358
- if ( typeof data[ 'success' ] !== 'undefined' ) {
359
- $( '.as3cf-url-preview' ).html( data[ 'url' ] );
360
- } else {
361
- alert( as3cf_i18n.get_url_preview_error + data[ 'error' ] );
362
- }
363
- }
364
- } );
365
- }
366
 
367
- /**
368
- * Return the serialized string of the settings form
369
- * excluding the bucket and region inputs as they get saved via AJAX
370
- *
371
- * @returns {string}
372
- */
373
- function serialized_form() {
374
- return $( '.as3cf-main-settings form' ).find( 'input:not(.no-compare)' ).serialize();
375
- }
376
 
377
- // save the original state of the form for comparison later
378
- saved_settings = serialized_form();
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
- // let the save settings submit happen as normal
381
- $( document ).on( 'submit', '.as3cf-main-settings form', function( event ) {
382
- // disable unload warning
383
- $( window ).off( 'beforeunload.as3cf-settings' );
384
  } );
385
 
386
- // prompt user with dialog if leaving the settings page with unsaved changes
387
- $( window ).on( 'beforeunload.as3cf-settings', function() {
388
- if ( serialized_form() !== saved_settings ) {
389
- return as3cf_i18n.save_alert;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  }
391
  } );
392
 
393
  } );
394
 
395
- })(jQuery);
1
+ (function( $, as3cfModal ) {
2
+
3
+ var savedSettings = {};
4
+ var bucketNamePattern = /[^a-z0-9.-]/;
5
+
6
+ var $tabs = $( '.as3cf-tab' );
7
+ var $activeTab;
8
+
9
+ /**
10
+ * Return the serialized string of the settings form
11
+ * excluding the bucket and region inputs as they get saved via AJAX
12
+ *
13
+ * @param string tab
14
+ *
15
+ * @returns {string}
16
+ */
17
+ function serializedForm( tab ) {
18
+ return $( '#' + tab + ' .as3cf-main-settings form' ).find( 'input:not(.no-compare)' ).serialize();
19
+ }
20
+
21
+ /**
22
+ * Set checkbox
23
+ *
24
+ * @param string checkbox_wrap
25
+ */
26
+ function setCheckbox( checkbox_wrap ) {
27
+ var $switch = $( '#' + checkbox_wrap );
28
+ var $checkbox = $switch.find( 'input[type=checkbox]' );
29
+
30
+ $switch.toggleClass( 'on' ).find( 'span' ).toggleClass( 'checked' );
31
+ var switchOn = $switch.find( 'span.on' ).hasClass( 'checked' );
32
+ $checkbox.attr( 'checked', switchOn ).trigger( 'change' );
33
+ }
34
+
35
+ as3cf.tabs = {
36
+ /**
37
+ * Toggle settings tab
38
+ *
39
+ * @param string hash
40
+ * @param bool persist_updated_notice
41
+ */
42
+ toggle: function( hash, persist_updated_notice ) {
43
+ $tabs.hide();
44
+ $activeTab = $( '#tab-' + hash );
45
+ $activeTab.show();
46
+ $( '.nav-tab' ).removeClass( 'nav-tab-active' );
47
+ $( 'a.nav-tab[data-tab="' + hash + '"]' ).addClass( 'nav-tab-active' );
48
+ $( '.aws-main' ).attr( 'data-tab', hash );
49
+ if ( $activeTab.attr( 'data-prefix' ) ) {
50
+ as3cfModal.prefix = $activeTab.attr( 'data-prefix' );
51
+ }
52
+ if ( !persist_updated_notice ) {
53
+ $( '.as3cf-updated' ).removeClass( 'show' );
54
+ }
55
+ }
56
+ };
57
+
58
+ /**
59
+ * Load bucket list
60
+ *
61
+ * @param bool forceUpdate
62
+ */
63
+ function loadBucketList( forceUpdate ) {
64
+ if ( 'undefined' === typeof forceUpdate ) {
65
+ forceUpdate = false;
66
+ }
67
 
68
+ var $bucketList = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-bucket-list' );
69
+ var selectedBucket = $( '#' + as3cfModal.prefix + '-bucket' ).val();
70
 
71
+ if ( false === forceUpdate && $bucketList.find( 'li' ).length > 1 ) {
72
+ $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
73
+ $( '.as3cf-bucket-list a[data-bucket="' + selectedBucket + '"]' ).addClass( 'selected' );
 
 
74
 
75
+ scrollToSelectedBucket();
76
+ return;
77
+ }
78
 
79
+ $bucketList.html( '<li class="loading">' + $bucketList.attr( 'data-working' ) + '</li>' );
80
+
81
+ var data = {
82
+ action: as3cfModal.prefix + '-get-buckets',
83
+ _nonce: window[ as3cfModal.prefix.replace( /-/g, '_' ) ].nonces.get_buckets
84
+ };
85
+
86
+ $.ajax( {
87
+ url: ajaxurl,
88
+ type: 'POST',
89
+ dataType: 'JSON',
90
+ data: data,
91
+ error: function( jqXHR, textStatus, errorThrown ) {
92
+ $bucketList.html( '' );
93
+ showBucketError( as3cf.strings.get_buckets_error, errorThrown, 'as3cf-bucket-select' );
94
+ },
95
+ success: function( data, textStatus, jqXHR ) {
96
+ $bucketList.html( '' );
97
+
98
+ if ( typeof data[ 'success' ] !== 'undefined' ) {
99
  $( '.as3cf-bucket-error' ).hide();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ $( data[ 'buckets' ] ).each( function( idx, bucket ) {
102
+ var bucketClass = bucket.Name === selectedBucket ? 'selected' : '';
103
+ $bucketList.append( '<li><a class="' + bucketClass + '" href="#" data-bucket="' + bucket.Name + '"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> ' + bucket.Name + '</span><span class="spinner"></span></span></a></li>' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  } );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ scrollToSelectedBucket();
107
+ } else {
108
+ showBucketError( as3cf.strings.get_buckets_error, data[ 'error' ], 'as3cf-bucket-select' );
109
+ }
 
 
110
  }
111
+ } );
112
+ }
113
+
114
+ /**
115
+ * Scroll to selected bucket
116
+ */
117
+ function scrollToSelectedBucket() {
118
+ if ( !$( '.as3cf-bucket-list a.selected' ).length ) {
119
+ return;
120
+ }
121
 
122
+ var offset = $( 'ul.as3cf-bucket-list li' ).first().position().top + 150;
 
 
 
 
 
 
 
123
 
124
+ $( '.as3cf-bucket-list' ).animate( {
125
+ scrollTop: $( 'ul.as3cf-bucket-list li a.selected' ).position().top - offset
126
  } );
127
+ }
128
+
129
+ /**
130
+ * Reset bucket modal
131
+ */
132
+ function resetBucketModal() {
133
+ var $bucketContainer = $( '.as3cf-bucket-container.' + as3cfModal.prefix );
134
+
135
+ if ( false === $activeTab.hasClass( 'as3cf-has-bucket' ) || 'manual' === $( '#' + as3cfModal.prefix + '-bucket-select' ).val() ) {
136
+ $bucketContainer.find( '.as3cf-bucket-manual' ).show().siblings().hide();
137
+ $bucketContainer.find( '.bucket-actions.manual' ).show().siblings( '.bucket-actions' ).hide();
138
+ } else {
139
+ $bucketContainer.find( '.as3cf-bucket-select' ).show().siblings().hide();
140
+ $bucketContainer.find( '.bucket-actions.select' ).show().siblings( '.bucket-actions' ).hide();
141
+
142
+ loadBucketList();
143
+ }
144
 
145
+ $bucketContainer.find( '.as3cf-bucket-error' ).hide();
146
 
147
+ // Reset manual select value
148
+ var bucket = $( '#' + as3cfModal.prefix + '-bucket' ).val();
149
+ $bucketContainer.find( '.as3cf-bucket-manual .as3cf-bucket-name' ).val( bucket );
150
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+ /**
153
+ * Save manual bucket
154
+ */
155
+ function saveManualBucket() {
156
+ var $manualBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-manual-save-bucket-form' );
157
+ var $manualBucketInput = $manualBucketForm.find( '.as3cf-bucket-name' );
158
+ var $manualBucketButton = $manualBucketForm.find( 'button[type=submit]' );
159
+ var bucketName = $manualBucketInput.val();
160
+ var originalBucketText = $manualBucketButton.first().text();
161
 
162
+ if ( bucketName === $( '#' + as3cfModal.prefix + '-active-bucket' ).text() ) {
163
+ $( '.as3cf-bucket-error' ).hide();
164
+ $activeTab.addClass( 'as3cf-has-bucket' );
165
+ as3cfModal.close();
166
+ return;
167
+ }
168
+ $( '.as3cf-bucket-error' ).hide();
169
+ $manualBucketButton.text( $manualBucketButton.attr( 'data-working' ) );
170
+ $manualBucketButton.prop( 'disabled', true );
171
+
172
+ var data = {
173
+ action: as3cfModal.prefix + '-manual-save-bucket',
174
+ bucket_name: bucketName,
175
+ _nonce: window[ as3cfModal.prefix.replace( /-/g, '_' ) ].nonces.manual_bucket
176
+ };
177
+
178
+ $.ajax( {
179
+ url: ajaxurl,
180
+ type: 'POST',
181
+ dataType: 'JSON',
182
+ data: data,
183
+ error: function( jqXHR, textStatus, errorThrown ) {
184
+ $manualBucketButton.text( originalBucketText );
185
+ showBucketError( as3cf.strings.save_bucket_error, errorThrown, 'as3cf-bucket-manual' );
186
+ },
187
+ success: function( data, textStatus, jqXHR ) {
188
+ $manualBucketButton.text( originalBucketText );
189
+ $manualBucketButton.prop( 'disabled', false );
190
+ if ( typeof data[ 'success' ] !== 'undefined' ) {
191
+ bucketSelect( bucketName, data[ 'region' ], data[ 'can_write' ] );
192
+ $( '#' + as3cfModal.prefix + '-bucket-select' ).val( 'manual' );
193
+ $( '.as3cf-bucket-list a' ).removeClass( 'selected' ).filter( '[data-bucket=' + bucketName + ']' ).addClass( 'selected' );
194
+ } else {
195
+ showBucketError( as3cf.strings.save_bucket_error, data[ 'error' ], 'as3cf-bucket-manual' );
196
+ }
197
  }
198
+ } );
199
+ }
200
+
201
+ /**
202
+ * Save select bucket
203
+ *
204
+ * @param object $link
205
+ */
206
+ function saveSelectBucket( $link ) {
207
+ var $bucketList = $( '.as3cf-bucket-list' );
208
 
209
+ if ( $link.hasClass( 'selected' ) ) {
210
+ $activeTab.addClass( 'as3cf-has-bucket' );
211
+ as3cfModal.close();
212
+ return;
213
+ }
214
 
215
+ var previousBucket = $( '.as3cf-bucket-list a.selected' ).attr( 'data-bucket' );
216
+
217
+ $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
218
+ $link.addClass( 'selected' );
219
+
220
+ $bucketList.addClass( 'saving' );
221
+ $link.find( '.spinner' ).show().css( 'visibility', 'visible' );
222
+ var bucketName = $link.attr( 'data-bucket' );
223
+
224
+ var data = {
225
+ action: as3cfModal.prefix + '-save-bucket',
226
+ bucket_name: bucketName,
227
+ _nonce: window[ as3cfModal.prefix.replace( /-/g, '_' ) ].nonces.save_bucket
228
+ };
229
+
230
+ $.ajax( {
231
+ url: ajaxurl,
232
+ type: 'POST',
233
+ dataType: 'JSON',
234
+ data: data,
235
+ error: function( jqXHR, textStatus, errorThrown ) {
236
+ $bucketList.removeClass( 'saving' );
237
+ showBucketError( as3cf.strings.save_bucket_error, errorThrown, 'as3cf-bucket-select' );
238
+ $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
239
+ $( '.as3cf-bucket-list a[data-bucket="' + previousBucket + '"]' ).addClass( 'selected' );
240
+ },
241
+ success: function( data, textStatus, jqXHR ) {
242
+ $link.find( '.spinner' ).hide().css( 'visibility', 'hidden' );
243
+ $bucketList.removeClass( 'saving' );
244
+ if ( typeof data[ 'success' ] !== 'undefined' ) {
245
+ bucketSelect( bucketName, data[ 'region' ], data[ 'can_write' ] );
246
+ $( '#' + as3cfModal.prefix + '-bucket-select' ).val( '' );
247
+ } else {
248
+ showBucketError( as3cf.strings.save_bucket_error, data[ 'error' ], 'as3cf-bucket-select' );
249
  $( '.as3cf-bucket-list a' ).removeClass( 'selected' );
250
+ $( '.as3cf-bucket-list a[data-bucket="' + previousBucket + '"]' ).addClass( 'selected' );
 
 
 
 
 
 
 
 
 
 
 
251
  }
252
+ }
253
  } );
254
+ }
255
+
256
+ /**
257
+ * Disable bucket buttons
258
+ */
259
+ function disableBucketButtons() {
260
+ if ( 0 === $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-create-bucket-form' ).length ) {
261
+ return;
262
+ }
263
 
264
+ var $createBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-create-bucket-form' );
265
+ var $manualBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-manual-save-bucket-form' );
 
 
266
 
267
+ if ( $createBucketForm.find( '.as3cf-bucket-name' ).val().length < 3 ) {
268
+ $createBucketForm.find( 'button[type=submit]' ).attr( 'disabled', true );
269
+ }
270
+ else {
271
+ $createBucketForm.find( 'button[type=submit]' ).attr( 'disabled', false );
272
+ }
273
 
274
+ if ( $manualBucketForm.find( '.as3cf-bucket-name' ).val().length < 3 ) {
275
+ $manualBucketForm.find( 'button[type=submit]' ).attr( 'disabled', true );
276
+ }
277
+ else {
278
+ $manualBucketForm.find( 'button[type=submit]' ).attr( 'disabled', false );
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Show bucket error
284
+ *
285
+ * @param title
286
+ * @param error
287
+ * @param context
288
+ */
289
+ function showBucketError( title, error, context ) {
290
+ var $activeView = $( '.as3cf-bucket-container' ).children( ':visible' );
291
+ var $bucketError = $activeView.find( '.as3cf-bucket-error' );
292
+
293
+ context = ( 'undefined' === typeof context ) ? null : context;
294
+
295
+ if ( context && ! $activeView.hasClass( context ) ) {
296
+ return;
297
  }
298
 
299
+ $bucketError.find( 'span.title' ).html( title );
300
+ $bucketError.find( 'span.message' ).html( error );
301
+ $bucketError.show();
302
+ }
303
+
304
+ /**
305
+ * Select bucket
306
+ *
307
+ * @param string title
308
+ * @param string error
309
+ * @param bool canWrite
310
+ */
311
+ function bucketSelect( bucket, region, canWrite ) {
312
+ var $manualBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-manual-save-bucket-form' );
313
+ var $activeBucket = $( '#' + as3cfModal.prefix + '-active-bucket' );
314
+
315
+ if ( 'as3cf' === as3cfModal.prefix && '' === $activeBucket.text() ) {
316
+ // first time bucket select - enable main options by default
317
+ setCheckbox( 'copy-to-s3-wrap' );
318
+ setCheckbox( 'serve-from-s3-wrap' );
319
  }
320
 
321
+ $activeBucket.text( bucket );
322
+ $manualBucketForm.find( '.as3cf-bucket-name' ).val( bucket );
323
+ $( '#' + as3cfModal.prefix + '-bucket' ).val( bucket );
324
+ $( '#' + as3cfModal.prefix + '-region' ).val( region );
325
+ $( '.updated' ).not( '.as3cf-notice' ).show();
326
+
327
+ $activeTab.addClass( 'as3cf-has-bucket' );
328
+ // check permission on bucket
329
+ $activeTab.find( '.as3cf-can-write-error' ).toggle( !canWrite );
330
+ $activeTab.find( '.as3cf-bucket-error' ).hide();
331
+
332
+ if ( 'as3cf' === as3cfModal.prefix ) {
333
+ generateUrlPreview();
334
+ }
335
+
336
+ as3cfModal.close();
337
+ }
338
+
339
+ /**
340
+ * Generate URL preview
341
+ */
342
+ function generateUrlPreview() {
343
+ $( '.as3cf-url-preview' ).html( 'Generating...' );
344
+
345
+ var data = {
346
+ _nonce: as3cf.nonces.get_url_preview
347
+ };
348
+
349
+ $.each( $( '#tab-media .as3cf-main-settings form' ).serializeArray(), function( i, o ) {
350
+ var n = o.name,
351
+ v = o.value;
352
+ n = n.replace( '[]', '' );
353
+ data[ n ] = data[ n ] === undefined ? v : $.isArray( data[ n ] ) ? data[ n ].concat( v ) : [ data[ n ], v ];
354
+ } );
355
+
356
+ // overwrite the save action stored in the form
357
+ data[ 'action' ] = 'as3cf-get-url-preview';
358
+
359
+ $.ajax( {
360
+ url: ajaxurl,
361
+ type: 'POST',
362
+ dataType: 'JSON',
363
+ data: data,
364
+ error: function( jqXHR, textStatus, errorThrown ) {
365
+ alert( as3cf.strings.get_url_preview_error + errorThrown );
366
+ },
367
+ success: function( data, textStatus, jqXHR ) {
368
+ if ( typeof data[ 'success' ] !== 'undefined' ) {
369
+ $( '.as3cf-url-preview' ).html( data[ 'url' ] );
370
+ } else {
371
+ alert( as3cf.strings.get_url_preview_error + data[ 'error' ] );
372
+ }
373
  }
374
+ } );
375
+ }
376
+
377
+ /**
378
+ * Check for a valid bucket name
379
+ *
380
+ * Bucket names must be at least 3 and no more than 63 characters long.
381
+ * They can contain lowercase letters, numbers, periods and hyphens.
382
+ *
383
+ * @param string bucketName
384
+ *
385
+ * @return bool
386
+ */
387
+ function isValidBucketName( bucketName ) {
388
+ if ( bucketName.length < 3 || bucketName.length > 63 ) {
389
+ return false;
390
+ }
391
+ if ( true === bucketNamePattern.test( bucketName ) ) {
392
+ return false;
393
  }
394
 
395
+ return true;
396
+ }
397
+
398
+ /**
399
+ * Update invalid bucket name notice
400
+ *
401
+ * @param string bucketName
402
+ */
403
+ function updateBucketNameNotice( bucketName ) {
404
+ var message = null;
405
+
406
+ if ( true === bucketNamePattern.test( bucketName ) ) {
407
+ message = as3cf.strings.create_bucket_invalid_chars;
408
+ } else if ( bucketName.length < 3 ) {
409
+ message = as3cf.strings.create_bucket_name_short;
410
+ } else if ( bucketName.length > 63 ) {
411
+ message = as3cf.strings.create_bucket_name_long;
412
+ }
413
+
414
+ if ( message && bucketName.length > 0 ) {
415
+ $( '.as3cf-invalid-bucket-name' ).html( message );
416
+ } else {
417
+ $( '.as3cf-invalid-bucket-name' ).html( '' );
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Save create bucket
423
+ */
424
+ function saveCreateBucket() {
425
+ var $createBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-create-bucket-form' );
426
+ var $createBucketInput = $createBucketForm.find( '.as3cf-bucket-name' );
427
+ var $createBucketSelect = $createBucketForm.find( '.bucket-create-region' );
428
+ var $createBucketButton = $createBucketForm.find( 'button[type=submit]' );
429
+
430
+ var bucketName = $createBucketInput.val();
431
+ var origButtonText = $createBucketButton.text();
432
+
433
+ $( '.as3cf-bucket-error' ).hide();
434
+ $createBucketButton.text( $createBucketButton.attr( 'data-working' ) );
435
+ $createBucketButton.prop( 'disabled', true );
436
+
437
+ var data = {
438
+ action: as3cfModal.prefix + '-create-bucket',
439
+ bucket_name: bucketName,
440
+ _nonce: window[ as3cfModal.prefix.replace( /-/g, '_' ) ].nonces.create_bucket
441
+ };
442
+
443
+ if ( $createBucketSelect.val() ) {
444
+ data[ 'region' ] = $createBucketSelect.val();
445
+ }
446
+
447
+ $.ajax( {
448
+ url: ajaxurl,
449
+ type: 'POST',
450
+ dataType: 'JSON',
451
+ data: data,
452
+ error: function( jqXHR, textStatus, errorThrown ) {
453
+ $createBucketButton.text( origButtonText );
454
+ showBucketError( as3cf.strings.create_bucket_error, errorThrown, 'as3cf-bucket-create' );
455
+ },
456
+ success: function( data, textStatus, jqXHR ) {
457
+ $createBucketButton.text( origButtonText );
458
+ $createBucketButton.prop( 'disabled', false );
459
+ if ( typeof data[ 'success' ] !== 'undefined' ) {
460
+ bucketSelect( bucketName, data[ 'region' ], data[ 'can_write' ] );
461
+ // tidy up create bucket form
462
+ $( '.as3cf-bucket-select-region' ).hide();
463
+ $( '.as3cf-bucket-select-region' ).removeAttr( 'selected' );
464
+ $createBucketInput.val( '' );
465
+ $createBucketButton.attr( 'disabled', true );
466
+ } else {
467
+ showBucketError( as3cf.strings.create_bucket_error, data[ 'error' ], 'as3cf-bucket-create' );
468
+ }
469
  }
470
  } );
471
+ }
472
+
473
+ $( document ).ready( function() {
474
 
475
+ // Tabs
476
+ // --------------------
477
+
478
+ // Move any compatibility errors below the nav tabs
479
+ var $navTabs = $( '.wrap.aws-main .nav-tab-wrapper' );
480
+ $( '.aws-compatibility-notice, div.updated, div.error, div.notice' ).not( '.below-h2, .inline' ).insertAfter( $navTabs );
481
+
482
+ // check for hash in url and switch tabs accordingly
483
+ if ( window.location.hash ) {
484
+ var hash = window.location.hash.substring( 1 );
485
+ as3cf.tabs.toggle( hash, true );
486
+ } else {
487
+ // default settings tab
488
+ var defaultTab = 'media';
489
+ $activeTab = $( '#tab-' + defaultTab );
490
+ $( '.aws-main' ).attr( 'data-tab', defaultTab );
491
  }
492
 
493
+ $( '.aws-main' ).on( 'click', '.nav-tab', function( e ) {
494
+ e.preventDefault();
495
+ if ( $( this ).hasClass( 'nav-tab-active' ) ) {
496
+ return;
497
  }
498
+ var nextTab = $( this ).attr( 'data-tab' );
499
+ as3cf.tabs.toggle( nextTab );
500
+ if ( 'media' === nextTab ) {
501
+ // As it's the default remove the hash
502
+ window.location.hash = '';
503
+ if ( typeof window.history.replaceState === 'function' && '#' === window.location.href.slice( -1 ) ) {
504
+ // Strip the # if still on the end of the URL
505
+ history.replaceState( {}, '', window.location.href.slice( 0, -1 ) );
506
+ }
507
+ } else {
508
+ window.location.hash = nextTab;
509
+ }
510
+ } );
511
+
512
+ // Settings
513
+ // --------------------
514
+
515
+ // save the original state of the forms for comparison later
516
+ if ( $tabs.length ) {
517
+ $tabs.each( function( i, tab ) {
518
+ savedSettings[ tab.id ] = serializedForm( tab.id );
519
+ } );
520
  }
521
 
522
+ // prompt user with dialog if leaving the settings page with unsaved changes
523
+ $( window ).on( 'beforeunload.as3cf-settings', function() {
524
+ if ( $.isEmptyObject( savedSettings ) ) {
525
+ return;
526
+ }
527
+
528
+ var tab = $activeTab.attr( 'id' );
529
+
530
+ if ( serializedForm( tab ) !== savedSettings[ tab ] ) {
531
+ return as3cf.strings.save_alert;
532
+ }
533
  } );
534
 
535
+ // let the save settings submit happen as normal
536
+ $( document ).on( 'submit', '.as3cf-main-settings form', function( e ) {
537
+ // disable unload warning
538
+ $( window ).off( 'beforeunload.as3cf-settings' );
539
  } );
540
 
541
+ $( '.as3cf-switch' ).on( 'click', function( e ) {
542
+ if ( !$( this ).hasClass( 'disabled' ) ) {
543
+ setCheckbox( $( this ).attr( 'id' ) );
544
+ }
545
+ } );
546
+
547
+ $tabs.on( 'change', '.sub-toggle', function( e ) {
548
+ var setting = $( this ).attr( 'id' );
549
+ $( '.as3cf-setting.' + setting ).toggleClass( 'hide' );
550
  } );
551
 
552
  $( '.as3cf-domain' ).on( 'change', 'input[type="radio"]', function( e ) {
553
+ var $selected = $( this ).closest( 'input:radio[name="domain"]:checked' );
554
+ var domain = $selected.val();
555
+ var $cloudfront = $( this ).parents( '.as3cf-domain' ).find( '.as3cf-setting.cloudfront' );
556
+ var cloudfrontSelected = ( 'cloudfront' === domain );
557
+ $cloudfront.toggleClass( 'hide', !cloudfrontSelected );
 
558
  } );
559
 
560
  $( '.as3cf-ssl' ).on( 'change', 'input[type="radio"]', function( e ) {
573
  } );
574
 
575
  $( '.url-preview' ).on( 'change', 'input', function( e ) {
576
+ generateUrlPreview();
577
  } );
578
 
579
+ // Bucket select
580
+ // --------------------
581
 
582
+ // Move bucket errors
583
+ $( '#tab-media > .as3cf-bucket-error' ).detach().insertAfter( '.as3cf-bucket-container h3' );
 
584
 
585
+ // Action click handlers
586
+ $( 'body' ).on( 'click', '.bucket-action-manual', function( e ) {
587
+ e.preventDefault();
588
+ $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-bucket-manual' ).show().siblings().hide();
589
+ } );
590
+ $( 'body' ).on( 'click', '.bucket-action-browse', function( e ) {
591
+ e.preventDefault();
592
+ $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-bucket-select' ).show().siblings().hide();
593
 
594
+ loadBucketList();
595
+ } );
596
+ $( 'body' ).on( 'click', '.bucket-action-create', function( e ) {
597
+ e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
 
599
+ // Reset create bucket modal
600
+ $( '.as3cf-bucket-name' ).val( '' );
601
+ $( '.as3cf-invalid-bucket-name' ).html( '' );
 
 
 
 
 
 
602
 
603
+ $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-bucket-create' ).show().siblings().hide();
604
+ } );
605
+ $( 'body' ).on( 'click', '.bucket-action-cancel', function( e ) {
606
+ e.preventDefault();
607
+ resetBucketModal();
608
+ } );
609
+ $( 'body' ).on( 'click', '.bucket-action-save', function( e ) {
610
+ e.preventDefault();
611
+ saveManualBucket();
612
+ } );
613
+ $( 'body' ).on( 'click', '.as3cf-create-bucket-form button[type="submit"]', function( e ) {
614
+ e.preventDefault();
615
+ saveCreateBucket();
616
+ } );
617
 
618
+ // Bucket list refresh handler
619
+ $( 'body' ).on( 'click', '.bucket-action-refresh', function( e ) {
620
+ e.preventDefault();
621
+ loadBucketList( true );
622
  } );
623
 
624
+ // Bucket list click handler
625
+ $( 'body' ).on( 'click', '.as3cf-bucket-list a', function( e ) {
626
+ e.preventDefault();
627
+ saveSelectBucket( $( this ) );
628
+ } );
629
+
630
+ // Modal open
631
+ $( 'body' ).on( 'as3cf-modal-open', function( e, target ) {
632
+ if ( '.as3cf-bucket-container.' + as3cfModal.prefix === target ) {
633
+ // Reset modal
634
+ resetBucketModal();
635
+ // Change manual title text
636
+ var title = $( '.as3cf-bucket-manual h3' ).data( 'modal-title' );
637
+ $( '.as3cf-bucket-manual h3' ).text( title );
638
+ // Hide buttons
639
+ disableBucketButtons();
640
+ }
641
+ } );
642
+ disableBucketButtons();
643
+
644
+ // Validate bucket name on create
645
+ $( 'body' ).on( 'input keyup', '.as3cf-create-bucket-form .as3cf-bucket-name', function( e ) {
646
+ var bucketName = $( this ).val();
647
+ var $createBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-create-bucket-form' );
648
+
649
+ if ( isValidBucketName( bucketName ) ) {
650
+ $createBucketForm.find( 'button[type=submit]' ).removeAttr( 'disabled' );
651
+ } else {
652
+ $createBucketForm.find( 'button[type=submit]' ).attr( 'disabled', true );
653
+ }
654
+ updateBucketNameNotice( bucketName );
655
+ } );
656
+
657
+ // Check bucket name length on manual
658
+ $( 'body' ).on( 'input keyup', '.as3cf-manual-save-bucket-form .as3cf-bucket-name', function( e ) {
659
+ var $manualBucketForm = $( '.as3cf-bucket-container.' + as3cfModal.prefix + ' .as3cf-manual-save-bucket-form' );
660
+
661
+ if ( $manualBucketForm.find( '.as3cf-bucket-name' ).val().length < 3 ) {
662
+ $manualBucketForm.find( 'button[type=submit]' ).attr( 'disabled', true );
663
+ } else {
664
+ $manualBucketForm.find( 'button[type=submit]' ).removeAttr( 'disabled' );
665
  }
666
  } );
667
 
668
  } );
669
 
670
+ })( jQuery, as3cfModal );
assets/js/script.min.js CHANGED
@@ -1 +1 @@
1
- !function(a){var b;a(document).ready(function(){function c(){a(".as3cf-bucket-error").hide(),j.html('<li class="loading">'+j.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){j.html(""),e(as3cf_i18n.get_buckets_error,c)},success:function(b){j.html(""),"undefined"!=typeof b.success?(a(b.buckets).each(function(a,c){var d=c.Name===b.selected?"selected":"";j.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()):e(as3cf_i18n.get_buckets_error,b.error)}})}function d(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a("ul.as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}}function e(b,c){a(".as3cf-bucket-error span.title").html(b),a(".as3cf-bucket-error span.message").html(c),a(".as3cf-bucket-error").show()}function f(b,c,d){""===a(".as3cf-active-bucket").text()&&(g("copy-to-s3-wrap"),g("serve-from-s3-wrap")),a(".as3cf-active-bucket").text(b),a("form.as3cf-manual-save-bucket-form .as3cf-bucket-name").val(b),a("#as3cf-bucket").val(b),a("#as3cf-region").val(c),a(".updated").not(".as3cf-notice").show(),d===!1&&a(".as3cf-can-write-error").show(),a(".as3cf-settings").addClass("as3cf-has-bucket"),h()}function g(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 h(){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)}})}function i(){return a(".as3cf-main-settings form").find("input:not(.no-compare)").serialize()}a(".as3cf-settings").each(function(){var b=a(this),d=a(".as3cf-bucket-list"),g=b.find(".as3cf-create-bucket-form"),h=b.find(".as3cf-manual-save-bucket-form");if(g.length){var i=g.find("button"),j=i.text();g.on("submit",function(b){b.preventDefault(),a(".as3cf-bucket-error").hide(),d.addClass("saving"),i.text(i.attr("data-working")),i.prop("disabled",!0);var k=g.find('input[name="bucket_name"]').val(),l={action:"as3cf-create-bucket",bucket_name:k,_nonce:as3cf_i18n.create_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:l,error:function(a,b,c){i.text(j),e(as3cf_i18n.create_bucket_error,c)},success:function(b){i.text(j),i.prop("disabled",!1),"undefined"!=typeof b.success?(f(k,b.region,b.can_write),g.find('input[name="bucket_name"]').val(""),a(".as3cf-bucket-list-wrapper").is(":visible")&&c(),a(".as3cf-bucket-list a").removeClass("selected"),d.removeClass("saving"),h.find('input[name="bucket_name"]').val(k)):e(as3cf_i18n.create_bucket_error,b.error)}})})}if(h.length){var k=h.find("button"),l=k.text();h.on("submit",function(b){b.preventDefault();var c=h.find('input[name="bucket_name"]').val();if(c===a(".as3cf-active-bucket").text())return a(".as3cf-bucket-error").hide(),void a(".as3cf-settings").addClass("as3cf-has-bucket");a(".as3cf-bucket-error").hide(),k.text(k.attr("data-working")),k.prop("disabled",!0);var d={action:"as3cf-manual-save-bucket",bucket_name:c,_nonce:as3cf_i18n.manual_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:d,error:function(a,b,c){k.text(l),e(as3cf_i18n.save_bucket_error,c)},success:function(b){k.text(l),k.prop("disabled",!1),"undefined"!=typeof b.success?(f(c,b.region,b.can_write),a(".as3cf-bucket-list a").removeClass("selected")):e(as3cf_i18n.save_bucket_error,b.error)}})})}var m=b.find(".as3cf-change-bucket");m.length&&m.on("click",function(b){b.preventDefault(),a(".updated").not(".as3cf-notice").hide(),a(".as3cf-can-write-error").hide(),a(".as3cf-settings").removeClass("as3cf-has-bucket"),a(".as3cf-bucket-list-wrapper").is(":visible")&&(c(),a(".as3cf-active-bucket").html&&a(".as3cf-cancel-bucket-select-wrap").show())});var n=b.find(".as3cf-refresh-buckets");n.length&&n.on("click",function(a){a.preventDefault(),c()});var o=b.find(".as3cf-cancel-bucket-select");o.length&&o.on("click",function(b){b.preventDefault(),a(".as3cf-bucket-error").hide(),a(".as3cf-settings").addClass("as3cf-has-bucket")})});var j=a(".as3cf-bucket-list");j.on("click","a",function(b){if(b.preventDefault(),a(this).hasClass("selected"))return void a(".as3cf-settings").addClass("as3cf-has-bucket");var c=this,d=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),a(c).addClass("selected"),j.addClass("saving"),a(c).find(".spinner").show();var g=a(c).attr("data-bucket"),h={action:"as3cf-save-bucket",bucket_name:g,_nonce:as3cf_i18n.save_bucket_nonce};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(b,c,f){j.removeClass("saving"),e(as3cf_i18n.save_bucket_error,f),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+d+'"]').addClass("selected")},success:function(b){a(c).find(".spinner").hide(),j.removeClass("saving"),"undefined"!=typeof b.success?f(g,b.region,b.can_write):(e(as3cf_i18n.save_bucket_error,b.error),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+d+'"]').addClass("selected"))}})}),a(".as3cf-switch").on("click","span",function(){if(!a(this).parent().hasClass("disabled")){var b=a(this).parent().attr("id");g(b)}}),a(".as3cf-settings").length&&!a(".as3cf-settings").hasClass("as3cf-has-bucket")&&a(".as3cf-bucket-list-wrapper").is(":visible")&&c(),a(".as3cf-settings").on("change",".sub-toggle",function(){var b=a(this).attr("id");a(".as3cf-setting."+b).toggleClass("hide")}),a(".as3cf-settings").on("click",".as3cf-manual-bucket-toggle",function(b){b.preventDefault(),a(".as3cf-bucket-select").addClass("manual")}),a(".as3cf-settings").on("click",".as3cf-bucket-list-toggle",function(b){b.preventDefault(),c(),a(".as3cf-bucket-select").removeClass("manual"),a(".as3cf-cancel-bucket-select-wrap").show()}),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(){h()}),b=i(),a(document).on("submit",".as3cf-main-settings form",function(){a(window).off("beforeunload.as3cf-settings")}),a(window).on("beforeunload.as3cf-settings",function(){return i()!==b?as3cf_i18n.save_alert:void 0})})}(jQuery);
1
+ !function(a,b){function c(b){return a("#"+b+" .as3cf-main-settings form").find("input:not(.no-compare)").serialize()}function d(b){var c=a("#"+b),d=c.find("input[type=checkbox]");c.toggleClass("on").find("span").toggleClass("checked");var e=c.find("span.on").hasClass("checked");d.attr("checked",e).trigger("change")}function e(c){"undefined"==typeof c&&(c=!1);var d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-list"),e=a("#"+b.prefix+"-bucket").val();if(!1===c&&d.find("li").length>1)return a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"),void f();d.html('<li class="loading">'+d.attr("data-working")+"</li>");var g={action:b.prefix+"-get-buckets",_nonce:window[b.prefix.replace(/-/g,"_")].nonces.get_buckets};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(a,b,c){d.html(""),k(as3cf.strings.get_buckets_error,c,"as3cf-bucket-select")},success:function(b){d.html(""),"undefined"!=typeof b.success?(a(".as3cf-bucket-error").hide(),a(b.buckets).each(function(a,b){var c=b.Name===e?"selected":"";d.append('<li><a class="'+c+'" href="#" data-bucket="'+b.Name+'"><span class="bucket"><span class="dashicons dashicons-portfolio"></span> '+b.Name+'</span><span class="spinner"></span></span></a></li>')}),f()):k(as3cf.strings.get_buckets_error,b.error,"as3cf-bucket-select")}})}function f(){if(a(".as3cf-bucket-list a.selected").length){var b=a("ul.as3cf-bucket-list li").first().position().top+150;a(".as3cf-bucket-list").animate({scrollTop:a("ul.as3cf-bucket-list li a.selected").position().top-b})}}function g(){var c=a(".as3cf-bucket-container."+b.prefix);!1===q.hasClass("as3cf-has-bucket")||"manual"===a("#"+b.prefix+"-bucket-select").val()?(c.find(".as3cf-bucket-manual").show().siblings().hide(),c.find(".bucket-actions.manual").show().siblings(".bucket-actions").hide()):(c.find(".as3cf-bucket-select").show().siblings().hide(),c.find(".bucket-actions.select").show().siblings(".bucket-actions").hide(),e()),c.find(".as3cf-bucket-error").hide();var d=a("#"+b.prefix+"-bucket").val();c.find(".as3cf-bucket-manual .as3cf-bucket-name").val(d)}function h(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find("button[type=submit]"),f=d.val(),g=e.first().text();if(f===a("#"+b.prefix+"-active-bucket").text())return a(".as3cf-bucket-error").hide(),q.addClass("as3cf-has-bucket"),void b.close();a(".as3cf-bucket-error").hide(),e.text(e.attr("data-working")),e.prop("disabled",!0);var h={action:b.prefix+"-manual-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.manual_bucket};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:h,error:function(a,b,c){e.text(g),k(as3cf.strings.save_bucket_error,c,"as3cf-bucket-manual")},success:function(c){e.text(g),e.prop("disabled",!1),"undefined"!=typeof c.success?(l(f,c.region,c.can_write),a("#"+b.prefix+"-bucket-select").val("manual"),a(".as3cf-bucket-list a").removeClass("selected").filter("[data-bucket="+f+"]").addClass("selected")):k(as3cf.strings.save_bucket_error,c.error,"as3cf-bucket-manual")}})}function i(c){var d=a(".as3cf-bucket-list");if(c.hasClass("selected"))return q.addClass("as3cf-has-bucket"),void b.close();var e=a(".as3cf-bucket-list a.selected").attr("data-bucket");a(".as3cf-bucket-list a").removeClass("selected"),c.addClass("selected"),d.addClass("saving"),c.find(".spinner").show().css("visibility","visible");var f=c.attr("data-bucket"),g={action:b.prefix+"-save-bucket",bucket_name:f,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.save_bucket};a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:g,error:function(b,c,f){d.removeClass("saving"),k(as3cf.strings.save_bucket_error,f,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected")},success:function(g){c.find(".spinner").hide().css("visibility","hidden"),d.removeClass("saving"),"undefined"!=typeof g.success?(l(f,g.region,g.can_write),a("#"+b.prefix+"-bucket-select").val("")):(k(as3cf.strings.save_bucket_error,g.error,"as3cf-bucket-select"),a(".as3cf-bucket-list a").removeClass("selected"),a('.as3cf-bucket-list a[data-bucket="'+e+'"]').addClass("selected"))}})}function j(){if(0!==a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form").length){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").attr("disabled",!1),d.find(".as3cf-bucket-name").val().length<3?d.find("button[type=submit]").attr("disabled",!0):d.find("button[type=submit]").attr("disabled",!1)}}function k(b,c,d){var e=a(".as3cf-bucket-container").children(":visible"),f=e.find(".as3cf-bucket-error");d="undefined"==typeof d?null:d,(!d||e.hasClass(d))&&(f.find("span.title").html(b),f.find("span.message").html(c),f.show())}function l(c,e,f){var g=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form"),h=a("#"+b.prefix+"-active-bucket");"as3cf"===b.prefix&&""===h.text()&&(d("copy-to-s3-wrap"),d("serve-from-s3-wrap")),h.text(c),g.find(".as3cf-bucket-name").val(c),a("#"+b.prefix+"-bucket").val(c),a("#"+b.prefix+"-region").val(e),a(".updated").not(".as3cf-notice").show(),q.addClass("as3cf-has-bucket"),q.find(".as3cf-can-write-error").toggle(!f),q.find(".as3cf-bucket-error").hide(),"as3cf"===b.prefix&&m(),b.close()}function m(){a(".as3cf-url-preview").html("Generating...");var b={_nonce:as3cf.nonces.get_url_preview};a.each(a("#tab-media .as3cf-main-settings form").serializeArray(),function(c,d){var e=d.name,f=d.value;e=e.replace("[]",""),b[e]=void 0===b[e]?f:a.isArray(b[e])?b[e].concat(f):[b[e],f]}),b.action="as3cf-get-url-preview",a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:b,error:function(a,b,c){alert(as3cf.strings.get_url_preview_error+c)},success:function(b){"undefined"!=typeof b.success?a(".as3cf-url-preview").html(b.url):alert(as3cf.strings.get_url_preview_error+b.error)}})}function n(a){return a.length<3||a.length>63?!1:!0===s.test(a)?!1:!0}function o(b){var c=null;!0===s.test(b)?c=as3cf.strings.create_bucket_invalid_chars:b.length<3?c=as3cf.strings.create_bucket_name_short:b.length>63&&(c=as3cf.strings.create_bucket_name_long),a(".as3cf-invalid-bucket-name").html(c&&b.length>0?c:"")}function p(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form"),d=c.find(".as3cf-bucket-name"),e=c.find(".bucket-create-region"),f=c.find("button[type=submit]"),g=d.val(),h=f.text();a(".as3cf-bucket-error").hide(),f.text(f.attr("data-working")),f.prop("disabled",!0);var i={action:b.prefix+"-create-bucket",bucket_name:g,_nonce:window[b.prefix.replace(/-/g,"_")].nonces.create_bucket};e.val()&&(i.region=e.val()),a.ajax({url:ajaxurl,type:"POST",dataType:"JSON",data:i,error:function(a,b,c){f.text(h),k(as3cf.strings.create_bucket_error,c,"as3cf-bucket-create")},success:function(b){f.text(h),f.prop("disabled",!1),"undefined"!=typeof b.success?(l(g,b.region,b.can_write),a(".as3cf-bucket-select-region").hide(),a(".as3cf-bucket-select-region").removeAttr("selected"),d.val(""),f.attr("disabled",!0)):k(as3cf.strings.create_bucket_error,b.error,"as3cf-bucket-create")}})}var q,r={},s=/[^a-z0-9.-]/,t=a(".as3cf-tab");as3cf.tabs={toggle:function(c,d){t.hide(),q=a("#tab-"+c),q.show(),a(".nav-tab").removeClass("nav-tab-active"),a('a.nav-tab[data-tab="'+c+'"]').addClass("nav-tab-active"),a(".aws-main").attr("data-tab",c),q.attr("data-prefix")&&(b.prefix=q.attr("data-prefix")),d||a(".as3cf-updated").removeClass("show")}},a(document).ready(function(){var f=a(".wrap.aws-main .nav-tab-wrapper");if(a(".aws-compatibility-notice, div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(f),window.location.hash){var k=window.location.hash.substring(1);as3cf.tabs.toggle(k,!0)}else{var l="media";q=a("#tab-"+l),a(".aws-main").attr("data-tab",l)}a(".aws-main").on("click",".nav-tab",function(b){if(b.preventDefault(),!a(this).hasClass("nav-tab-active")){var c=a(this).attr("data-tab");as3cf.tabs.toggle(c),"media"===c?(window.location.hash="","function"==typeof window.history.replaceState&&"#"===window.location.href.slice(-1)&&history.replaceState({},"",window.location.href.slice(0,-1))):window.location.hash=c}}),t.length&&t.each(function(a,b){r[b.id]=c(b.id)}),a(window).on("beforeunload.as3cf-settings",function(){if(!a.isEmptyObject(r)){var b=q.attr("id");return c(b)!==r[b]?as3cf.strings.save_alert:void 0}}),a(document).on("submit",".as3cf-main-settings form",function(){a(window).off("beforeunload.as3cf-settings")}),a(".as3cf-switch").on("click",function(){a(this).hasClass("disabled")||d(a(this).attr("id"))}),t.on("change",".sub-toggle",function(){var b=a(this).attr("id");a(".as3cf-setting."+b).toggleClass("hide")}),a(".as3cf-domain").on("change",'input[type="radio"]',function(){var b=a(this).closest('input:radio[name="domain"]:checked'),c=b.val(),d=a(this).parents(".as3cf-domain").find(".as3cf-setting.cloudfront"),e="cloudfront"===c;d.toggleClass("hide",!e)}),a(".as3cf-ssl").on("change",'input[type="radio"]',function(){var b=a('input:radio[name="ssl"]:checked').val();if("https"===b){var c=a('input:radio[name="domain"]:checked').val();"subdomain"===c&&a('input[name="domain"][value="path"]').attr("checked",!0),a(".subdomain-wrap input").attr("disabled",!0),a(".subdomain-wrap").addClass("disabled")}else a(".subdomain-wrap input").removeAttr("disabled"),a(".subdomain-wrap").removeClass("disabled")}),a(".url-preview").on("change","input",function(){m()}),a("#tab-media > .as3cf-bucket-error").detach().insertAfter(".as3cf-bucket-container h3"),a("body").on("click",".bucket-action-manual",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-manual").show().siblings().hide()}),a("body").on("click",".bucket-action-browse",function(c){c.preventDefault(),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-select").show().siblings().hide(),e()}),a("body").on("click",".bucket-action-create",function(c){c.preventDefault(),a(".as3cf-bucket-name").val(""),a(".as3cf-invalid-bucket-name").html(""),a(".as3cf-bucket-container."+b.prefix+" .as3cf-bucket-create").show().siblings().hide()}),a("body").on("click",".bucket-action-cancel",function(a){a.preventDefault(),g()}),a("body").on("click",".bucket-action-save",function(a){a.preventDefault(),h()}),a("body").on("click",'.as3cf-create-bucket-form button[type="submit"]',function(a){a.preventDefault(),p()}),a("body").on("click",".bucket-action-refresh",function(a){a.preventDefault(),e(!0)}),a("body").on("click",".as3cf-bucket-list a",function(b){b.preventDefault(),i(a(this))}),a("body").on("as3cf-modal-open",function(c,d){if(".as3cf-bucket-container."+b.prefix===d){g();var e=a(".as3cf-bucket-manual h3").data("modal-title");a(".as3cf-bucket-manual h3").text(e),j()}}),j(),a("body").on("input keyup",".as3cf-create-bucket-form .as3cf-bucket-name",function(){var c=a(this).val(),d=a(".as3cf-bucket-container."+b.prefix+" .as3cf-create-bucket-form");n(c)?d.find("button[type=submit]").removeAttr("disabled"):d.find("button[type=submit]").attr("disabled",!0),o(c)}),a("body").on("input keyup",".as3cf-manual-save-bucket-form .as3cf-bucket-name",function(){var c=a(".as3cf-bucket-container."+b.prefix+" .as3cf-manual-save-bucket-form");c.find(".as3cf-bucket-name").val().length<3?c.find("button[type=submit]").attr("disabled",!0):c.find("button[type=submit]").removeAttr("disabled")})})}(jQuery,as3cfModal);
assets/package.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "name": "amazon-s3-and-cloudfront",
3
- "version": "0.1.0",
4
- "devDependencies": {
5
- "grunt": "^0.4.4",
6
- "grunt-contrib-uglify": "^0.4.0",
7
- "grunt-contrib-watch": "^0.6.0",
8
- "grunt-contrib-compass": "^0.7.2",
9
- "grunt-contrib-jshint": "~0.10.0"
10
- }
11
- }
 
 
 
 
 
 
 
 
 
 
 
assets/sass/modal.scss ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Modals
3
+ */
4
+ #as3cf-overlay {
5
+ display: none;
6
+ position: fixed;
7
+ top: 0;
8
+ right: 0;
9
+ bottom: 0;
10
+ left: 0;
11
+ background-color: rgba(0, 0, 0, 0.5);
12
+ overflow: hidden;
13
+ overflow-y: auto;
14
+ z-index: 999999; // Needs to be higher than WP media modals
15
+ }
16
+
17
+ #as3cf-modal {
18
+ display: none;
19
+ position: relative;
20
+ width: 600px;
21
+ margin: 100px auto;
22
+ padding: 30px;
23
+ background-color: #eee;
24
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
25
+ font-size: 14px;
26
+ overflow: hidden;
27
+ z-index: 100000;
28
+
29
+ .close-as3cf-modal {
30
+ color: #999;
31
+ cursor: pointer;
32
+ font-family: "Times New Roman", serif;
33
+ font-size: 26px;
34
+ font-weight: 200;
35
+ position: absolute;
36
+ right: 18px;
37
+ top: 18px;
38
+
39
+ &:hover {
40
+ color: #666;
41
+ }
42
+ }
43
+
44
+ h3 {
45
+ margin: 0 0 20px;
46
+ font-weight: normal;
47
+ line-height: 1;
48
+ }
49
+
50
+ .error,
51
+ .notice,
52
+ .updated {
53
+ margin: 0 0 20px;
54
+ }
55
+
56
+ .actions {
57
+ margin: 20px -30px -30px;
58
+ padding: 20px 30px;
59
+ border-top: none;
60
+ background-color: #e3e3e3;
61
+ overflow: hidden;
62
+
63
+ .right {
64
+ margin-left: 15px;
65
+
66
+ &:last-of-type {
67
+ margin-left: 0;
68
+ }
69
+ }
70
+
71
+ button {
72
+ min-width: 90px;
73
+ }
74
+ }
75
+ }
76
+
77
+ body.as3cf-modal-open {
78
+ overflow: hidden;
79
+ padding-right: 15px;
80
+ }
assets/sass/styles.scss CHANGED
@@ -1,4 +1,43 @@
1
- .aws-main {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  .error {
3
  pre {
4
  background: #eaeaea;
@@ -13,11 +52,23 @@
13
  }
14
  }
15
 
16
- .as3cf-notice, .error {
17
- max-width: 935px;
18
- }
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- .updated {
21
  display: none;
22
  &.as3cf-notice, &.show {
23
  display: block;
@@ -25,13 +76,38 @@
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 {
@@ -39,14 +115,6 @@
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
  .object-prefix-desc {
52
  em {
@@ -54,23 +122,27 @@
54
  }
55
  }
56
 
57
- .as3cf-url-preview-wrap {
58
- background: #ffffff;
59
- text-align: center;
60
- padding: 20px 20px 10px;
61
- max-width: 610px;
62
- width: 100%;
63
- .as3cf-url-preview {
64
- margin-top: 5px;
65
- overflow-x: scroll;
66
- padding-bottom: 15px;
67
- }
68
- span {
69
- color: #aaa;
70
- text-transform: uppercase;
71
- font-weight: bold;
 
 
 
 
 
72
  }
73
- }
74
 
75
  .as3cf-ssl {
76
  p.info {
@@ -100,183 +172,108 @@
100
  }
101
  }
102
 
103
- .as3cf-switch {
104
- position: relative;
105
-
106
- &.disabled {
107
- span {
108
- cursor: default;
109
- margin: 1px 0 0 0;
110
- }
111
- span.checked {
112
- background: #F1F1F1;
113
- border: 1px solid #dddddd;
114
- color: #a3a3a3;
115
- }
116
- }
117
- span {
118
- -webkit-box-sizing: border-box;
119
- -moz-box-sizing: border-box;
120
- box-sizing: border-box;
121
- cursor: pointer;
122
- float: left;
123
- display: inline-block;
124
- height: 100%;
125
- font-size: 12px;
126
- line-height: 20px;
127
- border-radius: 2px;
128
- -webkit-border-radius: 2px;
129
- font-weight: bold;
130
- padding: 4px 8px;
131
- background: #F1F1F1;
132
- border: 1px solid #dddddd;
133
- color: #CCCCCC;
134
- margin: 1px -1px 0;
135
- z-index: 1;
136
-
137
- &.checked {
138
- background: #ffffff;
139
- border-color: #E4E4E4;
140
- color: #373737;
141
- padding: 5px 10px;
142
- margin: 0;
143
- z-index: 2;
144
- }
145
- }
146
- input[type="checkbox"] {
147
- position: absolute !important;
148
- top: 0;
149
- left: 0;
150
- opacity: 0;
151
- filter: alpha(opacity=0);
152
- z-index: -1;
153
- }
154
- }
155
-
156
- .as3cf-setting {
157
- &.hide{
158
- display: none;
159
- }
160
- }
161
-
162
- .as3cf-bucket-actions {
163
- position: absolute;
164
- right: 0;
165
- top: 2px;
166
 
167
- .as3cf-cancel-bucket-select-wrap {
168
- display: none;
169
- &:after {
170
- content: " | "
171
- }
172
- }
173
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- .as3cf-bucket-select {
176
- &.manual {
177
- .as3cf-manual-bucket-toggle,
178
- .as3cf-refresh-buckets,
179
- .as3cf-bucket-list-wrapper {
180
- display: none;
181
- }
182
- .as3cf-bucket-list-toggle,
183
- .as3cf-manual-save-bucket-wrapper {
184
- display: block;
185
- }
186
 
187
- .as3cf-cancel-bucket-select-wrap {
188
- &:after {
189
- content: ""
 
190
  }
191
- }
192
- }
193
-
194
- .as3cf-manual-bucket-toggle,
195
- .as3cf-bucket-list-wrapper {
196
- display: block;
197
- }
198
 
199
- .as3cf-refresh-buckets {
200
- display: inline;
 
 
 
 
 
 
201
  }
202
 
203
- .as3cf-bucket-list-toggle,
204
- .as3cf-manual-save-bucket-wrapper {
205
  display: none;
206
  }
207
  }
208
 
209
- .as3cf-bucket-list-wrapper {
210
- background: #fff;
211
- padding: 15px 20px;
212
- margin-bottom: 2em;
213
-
214
- .as3cf-bucket-list {
215
- max-height: 300px;
216
- overflow: auto;
217
- margin: 0;
218
 
219
- li {
220
- font-size: 14px;
221
- padding: 10px 0;
222
- }
223
- a {
224
- color: #444;
225
- text-decoration: none;
226
- &:hover, &.selected {
227
- color: #0074A2;
228
- }
229
- &.selected {
230
- font-weight: bold;
 
 
 
 
 
 
 
 
231
  }
232
- .bucket {
233
- float: left;
234
- clear: both;
235
- .dashicons {
236
- margin-right: 5px;
237
- }
238
  }
239
- .spinner {
240
- float: left;
241
- background-size: 15px 15px
242
  }
243
- }
244
- &.saving {
245
- opacity: 0.5;
246
- }
247
- }
248
- }
249
-
250
- input[type="text"].as3cf-bucket-name {
251
- width: 80%;
252
- }
253
-
254
- select.bucket {
255
- margin-bottom: 5px;
256
- width: 380px;
257
- }
258
-
259
- .form-table {
260
- td {
261
- padding-left: 0;
262
- padding-right: 0;
263
- &:first-child {
264
- vertical-align: top;
265
- min-width: 120px;
266
- }
267
- }
268
- h3 {
269
- font-weight: normal;
270
- text-transform: uppercase;
271
- margin: 0;
272
- }
273
- h4 {
274
- margin: 0;
275
- }
276
- .as3cf-border-bottom td {
277
- border-bottom: 1px solid #ddd;
278
  }
279
- }
280
 
281
  .as3cf-active-bucket {
282
  font-weight: bold;
@@ -346,16 +343,178 @@
346
  }
347
  }
348
 
349
- .as3cf-banner {
350
- img {
 
 
351
  display: block;
352
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  }
354
 
 
 
 
355
  .as3cf-sidebar {
356
  position: absolute;
357
- top: 17px;
358
- right: -312px;
359
  width: 292px;
360
 
361
  .block {
@@ -497,3 +656,17 @@
497
  margin-top: 50px;
498
  }
499
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /**
3
+ * AWS wrap
4
+ */
5
+ .aws-main.wrap {
6
+ &> h2 {
7
+ float: left;
8
+ }
9
+
10
+ .as3cf-notice, .as3cf-updated, .as3cf-error {
11
+ margin-bottom: 0;
12
+ -webkit-box-sizing: border-box;
13
+ -moz-box-sizing: border-box;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ .as3cf-error.fatal {
18
+ clear: both;
19
+ float: left;
20
+ }
21
+
22
+ h2.nav-tab-wrapper {
23
+ float: none;
24
+ margin-bottom: 0;
25
+ width: 650px;
26
+ margin-top: 10px;
27
+ padding-left: 5px;
28
+ padding-right: 0;
29
+
30
+ a.nav-tab-active {
31
+ color: #464646;
32
+ cursor: default;
33
+ }
34
+
35
+ a:focus {
36
+ -webkit-box-shadow: none;
37
+ box-shadow: none;
38
+ }
39
+ }
40
+
41
  .error {
42
  pre {
43
  background: #eaeaea;
52
  }
53
  }
54
 
55
+ &[data-tab="support"] {
56
+ .as3cf-notice, .error, .updated, .updated.show {
57
+ display: none;
58
+ }
59
+ .fatal .error, .dbrains-api-down {
60
+ display: block;
61
+ }
62
+ }
63
+ .as3cf-notice, .error, .updated {
64
+ max-width: 650px;
65
+ margin-top: 15px;
66
+ -webkit-box-sizing: border-box;
67
+ -moz-box-sizing: border-box;
68
+ box-sizing: border-box;
69
+ }
70
 
71
+ .as3cf-updated {
72
  display: none;
73
  &.as3cf-notice, &.show {
74
  display: block;
76
  }
77
  }
78
 
79
+ /**
80
+ * Bucket select
81
+ */
82
+ .as3cf-tab {
83
+ .as3cf-main-settings {
84
+ display: none;
85
+ }
86
+
87
+ .as3cf-bucket-container {
88
+ display: block;
89
+ }
90
+
91
+ &.as3cf-has-bucket {
92
+ .as3cf-main-settings {
93
+ display: block;
94
+ }
95
+
96
+ .as3cf-bucket-container {
97
+ display: none;
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Settings
104
+ */
105
+ .as3cf-tab {
106
+ display: none;
107
  position: relative;
108
  width: 650px;
 
109
 
110
+ .as3cf-main-settings {
 
111
  p {
112
  font-size: 13px;
113
  a {
115
  }
116
  }
117
  }
 
 
 
 
 
 
 
 
118
 
119
  .object-prefix-desc {
120
  em {
122
  }
123
  }
124
 
125
+ .as3cf-url-preview-wrap {
126
+ background: #fff;
127
+ text-align: center;
128
+ padding: 20px 0 0;
129
+ max-width: 650px;
130
+ width: 100%;
131
+ -webkit-box-sizing: border-box;
132
+ -moz-box-sizing: border-box;
133
+ box-sizing: border-box;
134
+
135
+ .as3cf-url-preview {
136
+ margin-top: 10px;
137
+ padding: 0 20px 10px;
138
+ overflow-x: scroll;
139
+ }
140
+ span {
141
+ color: #aaa;
142
+ text-transform: uppercase;
143
+ font-weight: bold;
144
+ }
145
  }
 
146
 
147
  .as3cf-ssl {
148
  p.info {
172
  }
173
  }
174
 
175
+ .as3cf-switch {
176
+ position: relative;
177
+ display: inline-block;
178
+ padding: 2px;
179
+ overflow: hidden;
180
+ border-radius: 2px;
181
+ -webkit-border-radius: 2px;
182
+ background-color: #d4d3d3;
183
+ cursor: pointer;
184
+
185
+ &.on {
186
+ background-color: #ade7b5;
187
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ span {
190
+ visibility: hidden;
191
+ -webkit-box-sizing: border-box;
192
+ -moz-box-sizing: border-box;
193
+ box-sizing: border-box;
194
+ float: left;
195
+ display: inline-block;
196
+ height: 100%;
197
+ font-size: 12px;
198
+ line-height: 20px;
199
+ border-radius: 2px;
200
+ -webkit-border-radius: 2px;
201
+ font-weight: bold;
202
+ padding: 4px 8px;
203
+ background: #fff;
204
+ color: #8d8d8d;
205
+ z-index: 1;
206
+
207
+ &.on {
208
+ color: #82d78b;
209
+ }
210
+
211
+ &.checked {
212
+ visibility: visible;
213
+ }
214
+ }
215
 
216
+ &.disabled {
217
+ cursor: default;
218
+ background: #e6e6e6;
 
 
 
 
 
 
 
 
219
 
220
+ span {
221
+ background: #f1f1f1;
222
+ color: #d6d6d6;
223
+ }
224
  }
 
 
 
 
 
 
 
225
 
226
+ input[type="checkbox"] {
227
+ position: absolute !important;
228
+ top: 0;
229
+ left: 0;
230
+ opacity: 0;
231
+ filter: alpha(opacity=0);
232
+ z-index: -1;
233
+ }
234
  }
235
 
236
+ .as3cf-setting {
237
+ &.hide{
238
  display: none;
239
  }
240
  }
241
 
242
+ h3 {
243
+ font-weight: normal;
244
+ text-transform: uppercase;
245
+ margin: 15px 0;
246
+ }
 
 
 
 
247
 
248
+ .form-table {
249
+ margin: 0;
250
+ tr {
251
+ &.as3cf-border-bottom td {
252
+ border-bottom: 1px solid #ddd;
253
+ padding: 20px 0px;
254
+ }
255
+ &.as3cf-setting-title td {
256
+ padding-bottom: 0;
257
+ }
258
+ &.as3cf-setting-title:first-child td {
259
+ padding-top: 20px;
260
+ }
261
+ td {
262
+ padding: 15px 0;
263
+ &:first-child {
264
+ vertical-align: top;
265
+ min-width: 120px;
266
+ }
267
+ }
268
  }
269
+ h3 {
270
+ padding: 0;
271
+ margin: 0;
 
 
 
272
  }
273
+ h4 {
274
+ margin: 0;
 
275
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  }
 
277
 
278
  .as3cf-active-bucket {
279
  font-weight: bold;
343
  }
344
  }
345
 
346
+ /**
347
+ * Media tab
348
+ */
349
+ #tab-media {
350
  display: block;
351
+
352
+ .as3cf-main-settings {
353
+ display: none;
354
+ }
355
+
356
+ .as3cf-bucket-container {
357
+ display: block;
358
+ }
359
+
360
+ &.as3cf-has-bucket {
361
+ .as3cf-main-settings {
362
+ display: block;
363
+ }
364
+
365
+ .as3cf-bucket-container {
366
+ display: none;
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Bucket select
373
+ */
374
+ .as3cf-bucket-container {
375
+ h3 {
376
+ line-height: 1.3;
377
+ text-transform: none;
378
+ }
379
+
380
+ a:focus {
381
+ -webkit-box-shadow: none;
382
+ box-shadow: none;
383
+ outline: none;
384
+ }
385
+
386
+ input[type=text] {
387
+ box-sizing: border-box;
388
+ width: 100%;
389
+ }
390
+
391
+ select {
392
+ box-sizing: border-box;
393
+ width: 50%;
394
+ }
395
+
396
+ .form-table {
397
+ td {
398
+ padding: 5px 0;
399
+
400
+ &:first-child {
401
+ width: 100px;
402
+ line-height: 30px;
403
+ vertical-align: top;
404
+ }
405
+ }
406
+ }
407
+
408
+ .as3cf-invalid-bucket-name {
409
+ font-size: 12px;
410
+ color: #a00;
411
+ }
412
+
413
+ .bucket-actions {
414
+ margin: 15px 0;
415
+ border-top: 1px solid #ccc;
416
+ padding-top: 15px;
417
+ overflow: hidden;
418
+
419
+ button,
420
+ .right {
421
+ float: right;
422
+ margin-right: 0;
423
+ }
424
+
425
+ span {
426
+ display: inline-block;
427
+ margin-right: 20px;
428
+ line-height: 28px;
429
+ }
430
+
431
+ .bucket-action-cancel {
432
+ color: #a00;
433
+ text-decoration: none;
434
+
435
+ &:hover {
436
+ color: red;
437
+ }
438
+ }
439
+ }
440
+
441
+ .as3cf-bucket-list {
442
+ padding: 15px;
443
+ max-height: 200px;
444
+ overflow-x: hidden;
445
+ overflow-y: auto;
446
+ background-color: #fff;
447
+ font-size: 14px;
448
+
449
+ li:last-of-type {
450
+ margin-bottom: 0;
451
+ }
452
+
453
+ a {
454
+ color: #444;
455
+ text-decoration: none;
456
+
457
+ &:hover {
458
+ color: #0074A2;
459
+ }
460
+
461
+ &.selected {
462
+ font-weight: bold;
463
+ color: #0074A2;
464
+ }
465
+
466
+ .dashicons {
467
+ margin-right: 5px;
468
+ }
469
+ }
470
+ }
471
+
472
+ .as3cf-bucket-select,
473
+ .as3cf-bucket-create {
474
+ display: none;
475
+ }
476
+
477
+ .bucket-actions.select {
478
+ display: none;
479
+ }
480
+ }
481
+
482
+ .as3cf-tab {
483
+ display: none;
484
+ }
485
+
486
+ #tab-media {
487
+ display: block;
488
+ }
489
+
490
+ #tab-support {
491
+ .as3cf-sidebar {
492
+ top: 11px;
493
+ }
494
+
495
+ .support-section {
496
+ border-bottom: 1px solid #ccc;
497
+ padding-bottom: 20px;
498
+ margin-bottom: 20px;
499
+ }
500
+
501
+ .debug {
502
+ textarea {
503
+ width: 100%;
504
+ min-height: 200px;
505
+ font-family: Consolas, Monaco, monospace;
506
+ margin-bottom: 5px;
507
+ }
508
+ }
509
  }
510
 
511
+ /**
512
+ * Sidebar
513
+ */
514
  .as3cf-sidebar {
515
  position: absolute;
516
+ top: 25px;
517
+ left: 670px;
518
  width: 292px;
519
 
520
  .block {
656
  margin-top: 50px;
657
  }
658
  }
659
+
660
+ /**
661
+ * Misc
662
+ */
663
+ .as3cf-banner {
664
+ img {
665
+ display: block;
666
+ }
667
+ }
668
+
669
+ .aws-compatibility-notice.error {
670
+ clear: both;
671
+ margin: 5px 20px 5px 0;
672
+ }
classes/amazon-s3-and-cloudfront.php CHANGED
@@ -1,16 +1,56 @@
1
  <?php
2
- use Aws\S3\S3Client;
3
 
4
  class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
 
 
 
 
5
  private $aws;
 
 
 
 
6
  private $s3client;
7
 
 
 
 
8
  protected $plugin_title;
 
 
 
 
9
  protected $plugin_menu_title;
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  const DEFAULT_ACL = 'public-read';
12
  const PRIVATE_ACL = 'private';
13
  const DEFAULT_EXPIRES = 900;
 
14
 
15
  const SETTINGS_KEY = 'tantan_wordpress_s3';
16
 
@@ -35,7 +75,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
35
  * @param string $plugin_file_path
36
  */
37
  function init( $plugin_file_path ) {
38
- $this->plugin_title = __( 'Amazon S3 and CloudFront', 'as3cf' );
 
39
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
40
 
41
  // fire up the plugin upgrade checker
@@ -45,27 +86,66 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
45
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
46
  add_action( 'wp_ajax_as3cf-save-bucket', array( $this, 'ajax_save_bucket' ) );
47
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
48
- add_action( 'wp_ajax_as3cf-manual-save-bucket', array( $this, 'ajax_manual_save_bucket' ) );
49
  add_action( 'wp_ajax_as3cf-get-url-preview', array( $this, 'ajax_get_url_preview' ) );
50
 
 
 
 
 
 
51
  add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 );
52
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
53
- add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 100, 2 );
54
  add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
55
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
 
56
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
57
- add_filter( 'as3cf_get_attached_file_copy_back_to_local', array( $this, 'regenerate_thumbnails_get_attached_file' ) );
 
 
 
58
 
59
  load_plugin_textdomain( 'as3cf', false, dirname( plugin_basename( $plugin_file_path ) ) . '/languages/' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
  /**
63
  * Accessor for a plugin setting with conditions to defaults and upgrades
64
  *
65
  * @param string $key
66
- * @param string $default
67
  *
68
- * @return int|mixed|string|void
69
  */
70
  function get_setting( $key, $default = '' ) {
71
  // use settings from $_POST when generating URL preview via AJAX
@@ -89,6 +169,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
89
  return '1';
90
  }
91
 
 
 
 
 
 
92
  // Default object prefix
93
  if ( 'object-prefix' == $key && ! isset( $settings['object-prefix'] ) ) {
94
  return $this->get_default_object_prefix();
@@ -115,9 +200,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
115
  // Region of bucket if not already retrieved
116
  if ( 'region' == $key && ! isset( $settings['region'] ) ) {
117
  $bucket = $this->get_setting( 'bucket' );
118
- $region = $this->get_bucket_region( $bucket );
119
- if ( is_wp_error( $region ) ) {
120
- $region = '';
121
  }
122
 
123
  return $region;
@@ -156,7 +241,6 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
156
  }
157
 
158
  if ( 'bucket' == $key && defined( 'AS3CF_BUCKET' ) ) {
159
-
160
  return AS3CF_BUCKET;
161
  }
162
 
@@ -165,16 +249,36 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
165
  return apply_filters( 'as3cf_setting_' . $key, $value );
166
  }
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  /**
169
  * Return the default object prefix
170
  *
171
  * @return string
172
  */
173
  function get_default_object_prefix() {
 
 
 
 
174
  $uploads = wp_upload_dir();
175
- $parts = parse_url( $uploads['baseurl'] );
 
176
 
177
- return substr( $parts['path'], 1 ) . '/';
178
  }
179
 
180
  /**
@@ -186,24 +290,58 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
186
  return apply_filters( 'as3cf_allowed_mime_types', get_allowed_mime_types() );
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  /**
190
  * Generate a preview of the URL of files uploaded to S3
191
  *
 
192
  * @param string $suffix
193
  *
194
  * @return string
195
  */
196
- function get_url_preview( $suffix = 'photo.jpg' ) {
197
  $scheme = $this->get_s3_url_scheme();
198
  $bucket = $this->get_setting( 'bucket' );
199
  $path = $this->get_file_prefix();
200
  $region = $this->get_setting( 'region' );
 
 
 
201
  $domain = $this->get_s3_url_domain( $bucket, $region );
202
 
203
  $url = $scheme . '://' . $domain . '/' . $path . $suffix;
204
 
205
  // replace hyphens with non breaking hyphens for formatting
206
- $url = str_replace( '-', '&#8209;', $url );
 
 
207
 
208
  return $url;
209
  }
@@ -216,11 +354,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
216
 
217
  $url = $this->get_url_preview();
218
 
219
- echo json_encode( array(
220
  'success' => '1',
221
  'url' => $url,
222
- ) );
223
- exit;
 
224
  }
225
 
226
  /**
@@ -264,18 +403,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
264
  /**
265
  * Delete bulk objects from an S3 bucket
266
  *
267
- * @param string $region
268
- * @param string $bucket
269
- * @param array $objects
270
- * @param bool $log_error
271
- * @param bool $return_on_error
 
 
272
  */
273
- function delete_s3_objects( $region, $bucket, $objects, $log_error = false, $return_on_error = false ) {
274
  try {
275
- $this->get_s3client( $region )->deleteObjects( array(
276
- 'Bucket' => $bucket,
277
- 'Objects' => $objects,
278
- ) );
279
  } catch ( Exception $e ) {
280
  if ( $log_error ) {
281
  error_log( 'Error removing files from S3: ' . $e->getMessage() );
@@ -286,59 +427,112 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
286
  }
287
  }
288
 
289
- function delete_attachment( $post_id ) {
290
- if ( ! $this->is_plugin_setup() ) {
291
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  }
 
293
 
294
- if ( ! ( $s3object = $this->get_attachment_s3_info( $post_id ) ) ) {
295
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  }
297
 
 
 
298
  $bucket = $s3object['bucket'];
299
  $region = $this->get_s3object_region( $s3object );
300
  if ( is_wp_error( $region ) ) {
301
- error_log( 'Error retrieving region: ' . $region->get_error_message() );
302
  $region = '';
303
  }
304
 
305
- $amazon_path = dirname( $s3object['key'] );
306
- $objects = array();
307
 
308
- // remove intermediate images
309
- $this->prepare_intermediate_images_to_remove( $post_id, $objects, $amazon_path );
310
- // remove backup images
311
- $this->prepare_backup_size_images_to_remove( $post_id, $objects, $amazon_path );
 
 
 
 
 
 
 
312
 
313
- // Try removing any @2x images but ignore any errors
314
- if ( $objects ) {
315
- $hidpi_images = array();
316
- foreach ( $objects as $object ) {
317
- $hidpi_images[] = array(
318
- 'Key' => $this->get_hidpi_file_path( $object['Key'] )
319
- );
320
- }
321
 
322
- $this->delete_s3_objects( $region, $bucket, $hidpi_images );
 
 
 
 
 
 
 
 
 
323
  }
324
 
325
- // add main file to be deleted
326
- $objects[] = array(
327
- 'Key' => $s3object['key'],
328
- );
329
 
330
- $this->delete_s3_objects( $region, $bucket, $objects, true, true );
331
 
332
  delete_post_meta( $post_id, 'amazonS3_info' );
333
  }
334
 
 
 
 
 
 
 
 
 
 
335
  function wp_update_attachment_metadata( $data, $post_id ) {
336
- if ( ! $this->get_setting( 'copy-to-s3' ) || ! $this->is_plugin_setup() ) {
 
 
 
 
 
337
  return $data;
338
  }
339
 
340
  // allow S3 upload to be cancelled for any reason
341
- $pre = apply_filters( 'as3cf_pre_update_attachment_metadata', false, $data, $post_id );
342
  if ( false !== $pre ) {
343
  return $data;
344
  }
@@ -352,16 +546,29 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
352
  /**
353
  * Upload attachment to S3
354
  *
355
- * @param $post_id
356
- * @param $data
 
 
 
357
  *
358
  * @return array|WP_Error $s3object
359
  */
360
- function upload_attachment_to_s3( $post_id, $data = null ) {
361
  if ( is_null( $data ) ) {
362
  $data = wp_get_attachment_metadata( $post_id, true );
363
  }
364
 
 
 
 
 
 
 
 
 
 
 
365
  $type = get_post_mime_type( $post_id );
366
  $allowed_types = $this->get_allowed_mime_types();
367
 
@@ -389,7 +596,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
389
  } else {
390
  // derive prefix from various settings
391
  if ( isset( $data['file'] ) ) {
392
- $time = untrailingslashit( dirname( $data['file'] ) );
393
  } else {
394
  $time = $this->get_attachment_folder_time( $post_id );
395
  $time = date( 'Y/m', $time );
@@ -400,11 +607,11 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
400
  // use bucket from settings
401
  $bucket = $this->get_setting( 'bucket' );
402
  $region = $this->get_setting( 'region' );
 
 
 
403
  }
404
 
405
- $file_path = get_attached_file( $post_id, true );
406
- $file_name = basename( $file_path );
407
-
408
  $acl = apply_filters( 'wps3_upload_acl', $acl, $type, $data, $post_id, $this ); // Old naming convention, will be deprecated soon
409
  $acl = apply_filters( 'as3cf_upload_acl', $acl, $data, $post_id );
410
 
@@ -419,35 +626,24 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
419
  $s3object['acl'] = $acl;
420
  }
421
 
422
- $s3client = $this->get_s3client( $region );
423
 
424
- $args = array(
425
  'Bucket' => $bucket,
426
  'Key' => $prefix . $file_name,
427
  'SourceFile' => $file_path,
428
  'ACL' => $acl,
429
- );
430
 
431
  // If far future expiration checked (10 years)
432
  if ( $this->get_setting( 'expires' ) ) {
433
  $args['Expires'] = date( 'D, d M Y H:i:s O', time() + 315360000 );
434
  }
435
 
436
- // if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
437
- // then we need to remove the edited image versions
438
- if ( isset( $_POST['do'] ) && 'restore' == $_POST['do'] && defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
439
- $objects_to_remove = array();
440
- // edited main file
441
- $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
442
- $objects_to_remove[] = array(
443
- 'Key' => path_join( $prefix, basename( $meta['file'] ) )
444
- );
445
- // edited resized image files
446
- $this->prepare_intermediate_images_to_remove( $post_id, $objects_to_remove, $prefix );
447
- $this->delete_s3_objects( $region, $bucket, $objects_to_remove, true );
448
- }
449
 
450
  $files_to_remove = array();
 
451
  if ( file_exists( $file_path ) ) {
452
  $files_to_remove[] = $file_path;
453
  try {
@@ -469,22 +665,24 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
469
 
470
  if ( isset( $data['thumb'] ) && $data['thumb'] ) {
471
  $path = str_replace( $file_name, $data['thumb'], $file_path );
472
- if ( file_exists( $path ) ) {
473
  $additional_images[] = array(
474
  'Key' => $prefix . $data['thumb'],
475
  'SourceFile' => $path,
476
  );
477
- $files_to_remove[] = $path;
 
478
  }
479
  } else if ( ! empty( $data['sizes'] ) ) {
480
  foreach ( $data['sizes'] as $size ) {
481
  $path = str_replace( $file_name, $size['file'], $file_path );
482
- if ( file_exists( $path ) ) {
483
  $additional_images[] = array(
484
  'Key' => $prefix . $size['file'],
485
  'SourceFile' => $path,
486
  );
487
- $files_to_remove[] = $path;
 
488
  }
489
  }
490
  }
@@ -520,12 +718,26 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
520
  }
521
 
522
  if ( $this->get_setting( 'remove-local-file' ) ) {
 
 
 
 
 
 
 
 
 
523
  $this->remove_local_files( $files_to_remove );
524
  }
525
 
526
  return $s3object;
527
  }
528
 
 
 
 
 
 
529
  function remove_local_files( $file_paths ) {
530
  foreach ( $file_paths as $path ) {
531
  if ( ! @unlink( $path ) ) {
@@ -556,6 +768,23 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
556
  return $object_version;
557
  }
558
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  // Media files attached to a post use the post's date
560
  // to determine the folder path they are placed in
561
  function get_attachment_folder_time( $post_id ) {
@@ -625,6 +854,9 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
625
 
626
  $bucket = $this->get_setting( 'bucket' );
627
  $region = $this->get_setting( 'region' );
 
 
 
628
 
629
  $s3client = $this->get_s3client( $region );
630
 
@@ -668,26 +900,29 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
668
  * Generate a link to download a file from Amazon S3 using query string
669
  * authentication. This link is only valid for a limited amount of time.
670
  *
671
- * @param int $post_id Post ID of the attachment
672
- * @param int $expires Seconds for the link to live
673
- * @param string $size Size of the image to get
 
674
  *
675
  * @return mixed|void|WP_Error
676
  */
677
- function get_secure_attachment_url( $post_id, $expires = null, $size = null ) {
678
  if ( is_null( $expires ) ) {
679
  $expires = self::DEFAULT_EXPIRES;
680
  }
681
- return $this->get_attachment_url( $post_id, $expires, $size );
682
  }
683
 
684
  /**
685
  * Return the scheme to be used in URLs
686
  *
 
 
687
  * @return string
688
  */
689
- function get_s3_url_scheme() {
690
- if ( $this->use_ssl() ) {
691
  $scheme = 'https';
692
  }
693
  else {
@@ -700,12 +935,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
700
  /**
701
  * Determine when to use https in URLS
702
  *
 
 
703
  * @return bool
704
  */
705
- function use_ssl( ) {
706
  $use_ssl = false;
707
 
708
- $ssl = $this->get_setting( 'ssl' );
 
 
709
 
710
  if ( 'request' == $ssl && is_ssl() ) {
711
  $use_ssl = true;
@@ -734,8 +973,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
734
  /**
735
  * Get the file prefix
736
  *
737
- * @param null $time
738
- * @param null $post_id
739
  *
740
  * @return string
741
  */
@@ -753,12 +992,24 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
753
  /**
754
  * Get the region specific prefix for S3 URL
755
  *
756
- * @param $region
 
757
  *
758
  * @return string
759
  */
760
- function get_s3_url_prefix( $region = '' ) {
761
- $prefix = ( '' == $region ) ? 's3' : 's3-' . $region;
 
 
 
 
 
 
 
 
 
 
 
762
 
763
  return $prefix;
764
  }
@@ -766,24 +1017,35 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
766
  /**
767
  * Get the S3 url for the files
768
  *
769
- * @param $bucket
770
  * @param string $region
771
  * @param int $expires
 
772
  *
773
  * @return mixed|string|void
774
  */
775
- function get_s3_url_domain( $bucket, $region = '', $expires = null ) {
776
- $domain = $this->get_setting( 'domain' );
 
 
777
 
778
- $prefix = $this->get_s3_url_prefix( $region );
 
 
 
 
 
 
 
 
779
 
780
- if ( 'cloudfront' == $domain && is_null( $expires ) && $this->get_setting( 'cloudfront' ) ) {
781
- $s3_domain = $this->get_setting( 'cloudfront' );
782
  }
783
- elseif ( 'virtual-host' == $domain ) {
784
  $s3_domain = $bucket;
785
  }
786
- elseif ( 'path' == $domain || $this->use_ssl() ) {
787
  $s3_domain = $prefix . '.amazonaws.com/' . $bucket;
788
  }
789
  else {
@@ -796,14 +1058,15 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
796
  /**
797
  * Get the url of the file from Amazon S3
798
  *
799
- * @param int $post_id Post ID of the attachment
800
- * @param int $expires Seconds for the link to live
801
- * @param string $size Size of the image to get
802
- * @param array $meta Pre retrieved _wp_attachment_metadata for the attachment
 
803
  *
804
  * @return bool|mixed|void|WP_Error
805
  */
806
- function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null ) {
807
  if ( ! $this->get_setting( 'serve-from-s3' ) ) {
808
  return false;
809
  }
@@ -843,7 +1106,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
843
  if ( ! is_null( $expires ) ) {
844
  try {
845
  $expires = time() + $expires;
846
- $secure_url = $this->get_s3client( $region )->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires );
847
  }
848
  catch ( Exception $e ) {
849
  return new WP_Error( 'exception', $e->getMessage() );
@@ -924,15 +1187,37 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
924
  return $file_path . $file_name;
925
  }
926
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
927
  /**
928
  * Return the S3 URL when the local file is missing
929
  * unless we know the calling process is and we are happy
930
  * to copy the file back to the server to be used
931
  *
932
- * @param $file
933
- * @param $attachment_id
934
  *
935
- * @return bool|mixed|void|WP_Error
936
  */
937
  function get_attached_file( $file, $attachment_id ) {
938
  if ( file_exists( $file ) || ! $this->get_setting( 'serve-from-s3' ) ) {
@@ -945,55 +1230,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
945
 
946
  $url = $this->get_attachment_url( $attachment_id );
947
 
948
- // the default behaviour is to return the S3 URL, however we can override this
949
- // and copy back the file to the local server for certain processes
950
- // where we know it will get removed again via wp_update_attachment_metadata
951
- $copy_back_to_local = apply_filters( 'as3cf_get_attached_file_copy_back_to_local', false, $file, $attachment_id );
952
- if ( false === $copy_back_to_local ) {
953
- // return S3 URL as a fallback
954
- return $url;
955
- }
956
-
957
- // fire up the filesystem API
958
- $filesystem = WP_Filesystem();
959
- global $wp_filesystem;
960
- if ( false === $filesystem || is_null( $wp_filesystem ) ) {
961
- error_log( __( 'There was an error attempting to access the file system', 'as3cf' ) );
962
-
963
- return $url;
964
- }
965
-
966
- // download the file from S3
967
- $temp_file = download_url( $url );
968
- // copy the temp file to the attachments location
969
- if ( ! $wp_filesystem->copy( $temp_file, $file ) ) {
970
- // fallback to url
971
- $file = $url;
972
- }
973
- // clear up temp file
974
- @unlink( $temp_file );
975
 
976
  return $file;
977
  }
978
 
979
  /**
980
- * Allow the Regenerate Thumbnails plugin to copy the S3 file back to the local
981
- * server when the file is missing on the server via get_attached_file
982
- *
983
- * @param $copy_back_to_local
984
  *
985
- * @return bool
986
  */
987
- function regenerate_thumbnails_get_attached_file( $copy_back_to_local ) {
988
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
989
- return $copy_back_to_local;
990
- }
991
-
992
- if ( isset( $_POST['action'] ) && 'regeneratethumbnail' == sanitize_key( $_POST['action'] ) ) { // input var okay
993
- return true;
994
- }
995
-
996
- return $copy_back_to_local;
997
  }
998
 
999
  function verify_ajax_request() {
@@ -1008,93 +1258,172 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1008
 
1009
  function ajax_check_bucket() {
1010
  if ( ! isset( $_POST['bucket_name'] ) || ! ( $bucket = sanitize_text_field( $_POST['bucket_name'] ) ) ) { // input var okay
1011
- echo json_encode( array( 'error' => __( 'No bucket name provided.', 'as3cf' ) ) );
1012
- exit;
 
1013
  }
1014
 
1015
- return $bucket;
1016
  }
1017
 
 
 
 
1018
  function ajax_create_bucket() {
1019
  $this->verify_ajax_request();
1020
 
1021
  $bucket = $this->ajax_check_bucket();
1022
 
1023
- $result = $this->create_bucket( $bucket );
1024
- if ( is_wp_error( $result ) ) {
1025
- $out = array( 'error' => $result->get_error_message() );
1026
  } else {
1027
- $region = $this->save_bucket( $bucket );
1028
-
1029
- if ( $region !== false ) {
1030
- $out = array(
1031
- 'success' => '1',
1032
- '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ),
1033
- 'region' => $region,
1034
- );
1035
- $out['can_write'] = $this->check_write_permission( $bucket, $region );
1036
- } else {
1037
- $out = array( 'error' => __( 'Failed to retrieve bucket region.', 'as3cf' ) );
1038
- }
1039
  }
1040
 
1041
- echo json_encode( $out );
1042
- exit;
1043
- }
1044
 
1045
- function create_bucket( $bucket_name ) {
1046
- try {
1047
- $this->get_s3client()->createBucket( array( 'Bucket' => $bucket_name ) );
1048
  }
1049
- catch ( Exception $e ) {
1050
- return new WP_Error( 'exception', $e->getMessage() );
1051
- }
1052
-
1053
- return true;
1054
- }
1055
-
1056
- function ajax_save_bucket() {
1057
- $this->verify_ajax_request();
1058
 
1059
- $bucket = $this->ajax_check_bucket();
 
1060
 
1061
- $region = $this->save_bucket( $bucket );
1062
 
1063
- if ( $region !== false ) {
1064
  $out = array(
1065
  'success' => '1',
 
1066
  'region' => $region,
1067
  );
1068
- $out['can_write'] = $this->check_write_permission( $bucket, $region );
 
 
 
 
 
 
 
 
 
 
1069
  } else {
1070
- $out = array( 'error' => __( 'Failed to retrieve bucket region.', 'as3cf' ) );
1071
  }
1072
 
1073
- echo json_encode( $out );
1074
- exit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1075
  }
1076
 
1077
  /**
1078
  * Save bucket and bucket's region
1079
  *
1080
- * @param string $bucket_name
1081
- * @param bool $manual if we are entering the bucket via the manual input form
 
1082
  *
1083
- * @return string|bool|WP_Error Region on success
1084
  */
1085
- function save_bucket( $bucket_name, $manual = false ) {
1086
  if ( $bucket_name ) {
1087
  $this->get_settings();
1088
- // first time bucket select - enable main options by default
1089
- if ( ! $this->get_setting( 'bucket' ) ) {
1090
- $this->set_setting( 'copy-to-s3', '1' );
1091
- $this->set_setting( 'serve-from-s3', '1' );
1092
- }
1093
  $this->set_setting( 'bucket', $bucket_name );
1094
- $region = $this->get_bucket_region( $bucket_name );
1095
- if ( is_wp_error( $region ) ) {
1096
- return false;
 
 
 
 
1097
  }
 
1098
  $this->set_setting( 'region', $region );
1099
 
1100
  if ( $manual ) {
@@ -1112,30 +1441,40 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1112
  return false;
1113
  }
1114
 
1115
- function ajax_manual_save_bucket() {
1116
- $this->verify_ajax_request();
1117
-
1118
- $bucket = $this->ajax_check_bucket();
1119
-
1120
- $region = $this->save_bucket( $bucket, true );
 
 
 
 
 
 
 
 
 
 
 
 
 
1121
 
1122
- if ( $region !== false ) {
1123
- $out = array(
1124
- 'success' => '1',
1125
- 'region' => $region,
1126
- );
1127
- $out['can_write'] = $this->check_write_permission( $bucket, $region );
1128
- } else {
1129
- $out = array( 'error' => __( 'Failed to retrieve bucket region.', 'as3cf' ) );
1130
  }
1131
 
1132
- echo json_encode( $out );
1133
- exit;
1134
  }
1135
 
 
 
 
 
 
1136
  function admin_menu( $aws ) {
1137
- $hook_suffix = $aws->add_page( $this->plugin_title, $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
1138
- add_action( 'load-' . $hook_suffix , array( $this, 'plugin_load' ) );
1139
  }
1140
 
1141
  /**
@@ -1167,7 +1506,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1167
  /**
1168
  * Get the region of a bucket
1169
  *
1170
- * @param $bucket
1171
  *
1172
  * @return string|WP_Error
1173
  */
@@ -1176,7 +1515,8 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1176
  $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
1177
  }
1178
  catch ( Exception $e ) {
1179
- error_log( sprintf( __( 'There was an error attempting to get the region of the bucket %s: %s', 'as3cf' ), $bucket, $e->getMessage() ) );
 
1180
 
1181
  return new WP_Error( 'exception', $e->getMessage() );
1182
  }
@@ -1247,12 +1587,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1247
  $out = array(
1248
  'success' => '1',
1249
  'buckets' => $result,
1250
- 'selected' => $this->get_setting( 'bucket' )
1251
  );
1252
  }
1253
 
1254
- echo json_encode( $out );
1255
- exit;
1256
  }
1257
 
1258
  /**
@@ -1277,7 +1615,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1277
  * @param string $bucket
1278
  * @param string $region
1279
  *
1280
- * @return bool
1281
  */
1282
  function check_write_permission( $bucket = null, $region = null ) {
1283
  if ( is_null( $bucket ) ) {
@@ -1287,6 +1625,12 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1287
  }
1288
  }
1289
 
 
 
 
 
 
 
1290
  $file_name = 'as3cf-permission-check.txt';
1291
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'as3cf' );
1292
 
@@ -1304,6 +1648,10 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1304
  // need to set region for buckets in non default region
1305
  if ( is_null( $region ) ) {
1306
  $region = $this->get_setting( 'region' );
 
 
 
 
1307
  }
1308
  // attempt to create the test file
1309
  $this->get_s3client( $region, true )->putObject( $args );
@@ -1314,51 +1662,142 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1314
  ) );
1315
  $can_write = true;
1316
  } catch ( Exception $e ) {
 
 
 
 
 
 
 
 
1317
  // write permission not found
1318
  $can_write = false;
1319
  }
1320
 
 
 
 
 
 
 
 
1321
  return $can_write;
1322
  }
1323
 
1324
- function plugin_load() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1325
  $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
 
1326
 
1327
- $src = plugins_url( 'assets/css/styles.css', $this->plugin_file_path );
1328
- wp_enqueue_style( 'as3cf-styles', $src, array(), $version );
1329
 
 
 
 
 
 
 
1330
  $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1331
 
 
 
 
1332
  $src = plugins_url( 'assets/js/script' . $suffix . '.js', $this->plugin_file_path );
1333
- wp_enqueue_script( 'as3cf-script', $src, array( 'jquery' ), $version, true );
1334
-
1335
- wp_localize_script( 'as3cf-script', 'as3cf_i18n', array(
1336
- 'create_bucket_prompt' => __( 'Bucket Name:', 'as3cf' ),
1337
- 'create_bucket_error' => __( 'Error creating bucket: ', 'as3cf' ),
1338
- 'create_bucket_nonce' => wp_create_nonce( 'as3cf-create-bucket' ),
1339
- 'manual_bucket_nonce' => wp_create_nonce( 'as3cf-manual-save-bucket' ),
1340
- 'get_buckets_error' => __( 'Error fetching buckets: ', 'as3cf' ),
1341
- 'get_buckets_nonce' => wp_create_nonce( 'as3cf-get-buckets' ),
1342
- 'save_bucket_error' => __( 'Error saving bucket: ', 'as3cf' ),
1343
- 'save_bucket_nonce' => wp_create_nonce( 'as3cf-save-bucket' ),
1344
- 'get_url_preview_nonce' => wp_create_nonce( 'as3cf-get-url-preview' ),
1345
- 'get_url_preview_error' => __( 'Error getting URL preview: ', 'as3cf' ),
1346
- 'save_alert' => __( 'The changes you made will be lost if you navigate away from this page', 'as3cf' ),
1347
- ) );
 
 
 
 
 
 
 
 
 
 
1348
 
1349
  $this->handle_post_request();
 
 
 
1350
  }
1351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1352
  function handle_post_request() {
 
 
 
 
1353
  if ( empty( $_POST['action'] ) || 'save' != sanitize_key( $_POST['action'] ) ) { // input var okay
1354
  return;
1355
  }
1356
 
1357
- if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'as3cf-save-settings' ) ) { // input var okay
1358
- die( __( "Cheatin' eh?", 'amazon-web-services' ) );
1359
  }
1360
 
1361
- $post_vars = array( 'bucket', 'region', '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' );
 
 
1362
 
1363
  foreach ( $post_vars as $var ) {
1364
  $this->remove_setting( $var );
@@ -1374,15 +1813,65 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1374
 
1375
  $this->save_settings();
1376
 
1377
- wp_redirect( 'admin.php?page=' . $this->plugin_slug . '&updated=1' );
 
1378
  exit;
1379
  }
1380
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1381
  /**
1382
  * Display the main settings page for the plugin
1383
  */
1384
  function render_page() {
1385
- $this->aws->render_view( 'header', array( 'page_title' => $this->plugin_title ) );
1386
 
1387
  $aws_client = $this->aws->get_client();
1388
 
@@ -1390,6 +1879,7 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1390
  $this->render_view( 'error-fatal', array( 'message' => $aws_client->get_error_message() ) );
1391
  }
1392
  else {
 
1393
  do_action( 'as3cf_pre_settings_render' );
1394
  $this->render_view( 'settings' );
1395
  do_action( 'as3cf_post_settings_render' );
@@ -1398,6 +1888,20 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1398
  $this->aws->render_view( 'footer' );
1399
  }
1400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1401
  /**
1402
  * Get the prefix path for the files
1403
  *
@@ -1406,17 +1910,16 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1406
  * @return string
1407
  */
1408
  function get_dynamic_prefix( $time = null ) {
1409
- $uploads = wp_upload_dir( $time );
1410
  $prefix = '';
1411
  if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
1412
- $prefix = str_replace( $this->get_base_upload_path(), '', $uploads['path'] );
 
1413
  }
1414
 
1415
  // support legacy MS installs (<3.5 since upgraded) for subsites
1416
- if ( is_multisite() && 1 != ( $blog_id = get_current_blog_id() ) && strpos( $prefix, 'sites/' ) === false ) {
1417
- $details = get_blog_details( $blog_id );
1418
- $sitename = basename( $details->siteurl );
1419
- $legacy_ms_prefix = $sitename . '/files/';
1420
  $legacy_ms_prefix = apply_filters( 'as3cf_legacy_ms_subsite_prefix', $legacy_ms_prefix, $details );
1421
  $prefix = '/' . trailingslashit( ltrim( $legacy_ms_prefix, '/' ) ) . ltrim( $prefix, '/' );
1422
  }
@@ -1463,13 +1966,419 @@ class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
1463
 
1464
  $blog_ids = array();
1465
  foreach ( $blogs as $blog ) {
1466
- if ( 1 == $blog['blog_id'] ) {
1467
- // ignore the first blog which doesn't have the ID in the table prefix
1468
- continue;
1469
- }
1470
  $blog_ids[] = $blog['blog_id'];
1471
  }
1472
 
1473
  return $blog_ids;
1474
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1475
  }
1
  <?php
 
2
 
3
  class Amazon_S3_And_CloudFront extends AWS_Plugin_Base {
4
+
5
+ /**
6
+ * @var Amazon_Web_Services
7
+ */
8
  private $aws;
9
+
10
+ /**
11
+ * @var Aws\S3\S3Client
12
+ */
13
  private $s3client;
14
 
15
+ /**
16
+ * @var string
17
+ */
18
  protected $plugin_title;
19
+
20
+ /**
21
+ * @var string
22
+ */
23
  protected $plugin_menu_title;
24
 
25
+ /**
26
+ * @var array
27
+ */
28
+ protected static $admin_notices = array();
29
+
30
+ /**
31
+ * @var
32
+ */
33
+ protected static $plugin_page;
34
+
35
+ /**
36
+ * @var string
37
+ */
38
+ protected $plugin_prefix = 'as3cf';
39
+
40
+ /**
41
+ * @var string
42
+ */
43
+ protected $default_tab = '';
44
+
45
+ /**
46
+ * @var array
47
+ */
48
+ public $hook_suffix;
49
+
50
  const DEFAULT_ACL = 'public-read';
51
  const PRIVATE_ACL = 'private';
52
  const DEFAULT_EXPIRES = 900;
53
+ const DEFAULT_REGION = 'us-east-1';
54
 
55
  const SETTINGS_KEY = 'tantan_wordpress_s3';
56
 
75
  * @param string $plugin_file_path
76
  */
77
  function init( $plugin_file_path ) {
78
+ self::$plugin_page = $this->plugin_slug;
79
+ $this->plugin_title = __( 'Offload S3', 'as3cf' );
80
  $this->plugin_menu_title = __( 'S3 and CloudFront', 'as3cf' );
81
 
82
  // fire up the plugin upgrade checker
86
  add_action( 'wp_ajax_as3cf-get-buckets', array( $this, 'ajax_get_buckets' ) );
87
  add_action( 'wp_ajax_as3cf-save-bucket', array( $this, 'ajax_save_bucket' ) );
88
  add_action( 'wp_ajax_as3cf-create-bucket', array( $this, 'ajax_create_bucket' ) );
89
+ add_action( 'wp_ajax_as3cf-manual-save-bucket', array( $this, 'ajax_save_bucket' ) );
90
  add_action( 'wp_ajax_as3cf-get-url-preview', array( $this, 'ajax_get_url_preview' ) );
91
 
92
+ // Admin notices
93
+ add_action( 'admin_notices', array( $this, 'maybe_show_admin_notices' ) );
94
+ add_action( 'network_admin_notices', array( $this, 'maybe_show_admin_notices' ) );
95
+ add_action( 'shutdown', array( $this, 'save_admin_notices' ) );
96
+
97
  add_filter( 'wp_get_attachment_url', array( $this, 'wp_get_attachment_url' ), 99, 2 );
98
  add_filter( 'wp_handle_upload_prefilter', array( $this, 'wp_handle_upload_prefilter' ), 1 );
99
+ add_filter( 'wp_update_attachment_metadata', array( $this, 'wp_update_attachment_metadata' ), 110, 2 );
100
  add_filter( 'wp_get_attachment_metadata', array( $this, 'wp_get_attachment_metadata' ), 10, 2 );
101
  add_filter( 'delete_attachment', array( $this, 'delete_attachment' ), 20 );
102
+ add_filter( 'update_attached_file', array( $this, 'update_attached_file' ), 100, 2 );
103
  add_filter( 'get_attached_file', array( $this, 'get_attached_file' ), 10, 2 );
104
+ add_filter( 'plugin_action_links', array( $this, 'plugin_actions_settings_link' ), 10, 2 );
105
+
106
+ // include compatibility code for other plugins
107
+ new AS3CF_Plugin_Compatibility( $this );
108
 
109
  load_plugin_textdomain( 'as3cf', false, dirname( plugin_basename( $plugin_file_path ) ) . '/languages/' );
110
+
111
+ // Register modal scripts and styles
112
+ $this->register_modal_assets();
113
+ }
114
+
115
+ /**
116
+ * Get the plugin title to be used in page headings
117
+ *
118
+ * @return string
119
+ */
120
+ function get_plugin_page_title() {
121
+ return apply_filters( 'as3cf_settings_page_title', $this->plugin_title );
122
+ }
123
+
124
+ /**
125
+ * Get the plugin prefix in slug format, ie. replace underscores with hyphens
126
+ *
127
+ * @return string
128
+ */
129
+ function get_plugin_prefix_slug() {
130
+ return str_replace( '_', '-', $this->plugin_prefix );
131
+ }
132
+
133
+ /**
134
+ * Get the nonce key for the settings form of the plugin
135
+ *
136
+ * @return string
137
+ */
138
+ function get_settings_nonce_key() {
139
+ return $this->get_plugin_prefix_slug() . '-save-settings';
140
  }
141
 
142
  /**
143
  * Accessor for a plugin setting with conditions to defaults and upgrades
144
  *
145
  * @param string $key
146
+ * @param mixed $default
147
  *
148
+ * @return int|mixed|string|WP_Error
149
  */
150
  function get_setting( $key, $default = '' ) {
151
  // use settings from $_POST when generating URL preview via AJAX
169
  return '1';
170
  }
171
 
172
+ // Turn on object versioning by default
173
+ if ( 'object-versioning' == $key && ! isset( $settings['object-versioning'] ) ) {
174
+ return '1';
175
+ }
176
+
177
  // Default object prefix
178
  if ( 'object-prefix' == $key && ! isset( $settings['object-prefix'] ) ) {
179
  return $this->get_default_object_prefix();
200
  // Region of bucket if not already retrieved
201
  if ( 'region' == $key && ! isset( $settings['region'] ) ) {
202
  $bucket = $this->get_setting( 'bucket' );
203
+ $region = $default;
204
+ if ( $bucket ) {
205
+ $region = $this->get_bucket_region( $bucket );
206
  }
207
 
208
  return $region;
241
  }
242
 
243
  if ( 'bucket' == $key && defined( 'AS3CF_BUCKET' ) ) {
 
244
  return AS3CF_BUCKET;
245
  }
246
 
249
  return apply_filters( 'as3cf_setting_' . $key, $value );
250
  }
251
 
252
+ /**
253
+ * Setter for a plugin setting with custom hooks
254
+ *
255
+ * @param string $key
256
+ * @param mixed $value
257
+ */
258
+ function set_setting( $key, $value ) {
259
+ // Run class specific hooks before the setting is saved
260
+ $this->pre_set_setting( $key, $value );
261
+
262
+ $value = apply_filters( 'as3cf_set_setting_' . $key, $value );
263
+
264
+ parent::set_setting( $key, $value );
265
+ }
266
+
267
  /**
268
  * Return the default object prefix
269
  *
270
  * @return string
271
  */
272
  function get_default_object_prefix() {
273
+ if ( is_multisite() ) {
274
+ return 'wp-content/uploads/';
275
+ }
276
+
277
  $uploads = wp_upload_dir();
278
+ $parts = parse_url( $uploads['baseurl'] );
279
+ $path = ltrim( $parts['path'], '/' );
280
 
281
+ return trailingslashit( $path );
282
  }
283
 
284
  /**
290
  return apply_filters( 'as3cf_allowed_mime_types', get_allowed_mime_types() );
291
  }
292
 
293
+ /**
294
+ * Wrapper for scheduling cron jobs
295
+ *
296
+ * @param string $hook
297
+ * @param null|string $interval Defaults to hook if not supplied
298
+ * @param array $args
299
+ */
300
+ function schedule_event( $hook, $interval = null, $args = array() ) {
301
+ if ( is_null( $interval ) ) {
302
+ $interval = $hook;
303
+ }
304
+ if ( ! wp_next_scheduled( $hook ) ) {
305
+ wp_schedule_event( current_time( 'timestamp' ), $interval, $hook, $args );
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Wrapper for clearing scheduled events for a specific cron job
311
+ *
312
+ * @param string $hook
313
+ */
314
+ function clear_scheduled_event( $hook ) {
315
+ $timestamp = wp_next_scheduled( $hook );
316
+ if ( $timestamp ) {
317
+ wp_unschedule_event( $timestamp, $hook );
318
+ }
319
+ }
320
+
321
  /**
322
  * Generate a preview of the URL of files uploaded to S3
323
  *
324
+ * @param bool $escape
325
  * @param string $suffix
326
  *
327
  * @return string
328
  */
329
+ function get_url_preview( $escape = true, $suffix = 'photo.jpg' ) {
330
  $scheme = $this->get_s3_url_scheme();
331
  $bucket = $this->get_setting( 'bucket' );
332
  $path = $this->get_file_prefix();
333
  $region = $this->get_setting( 'region' );
334
+ if ( is_wp_error( $region ) ) {
335
+ $region = '';
336
+ }
337
  $domain = $this->get_s3_url_domain( $bucket, $region );
338
 
339
  $url = $scheme . '://' . $domain . '/' . $path . $suffix;
340
 
341
  // replace hyphens with non breaking hyphens for formatting
342
+ if ( $escape ) {
343
+ $url = str_replace( '-', '&#8209;', $url );
344
+ }
345
 
346
  return $url;
347
  }
354
 
355
  $url = $this->get_url_preview();
356
 
357
+ $out = array(
358
  'success' => '1',
359
  'url' => $url,
360
+ );
361
+
362
+ $this->end_ajax( $out );
363
  }
364
 
365
  /**
403
  /**
404
  * Delete bulk objects from an S3 bucket
405
  *
406
+ * @param string $region
407
+ * @param string $bucket
408
+ * @param array $objects
409
+ * @param bool $log_error
410
+ * @param bool $return_on_error
411
+ * @param bool $force_new_s3_client if we are deleting in bulk, force new S3 client
412
+ * to cope with possible different regions
413
  */
414
+ function delete_s3_objects( $region, $bucket, $objects, $log_error = false, $return_on_error = false, $force_new_s3_client = false ) {
415
  try {
416
+ $this->get_s3client( $region, $force_new_s3_client )->deleteObjects( array(
417
+ 'Bucket' => $bucket,
418
+ 'Objects' => $objects,
419
+ ) );
420
  } catch ( Exception $e ) {
421
  if ( $log_error ) {
422
  error_log( 'Error removing files from S3: ' . $e->getMessage() );
427
  }
428
  }
429
 
430
+ /**
431
+ * Remove any HDiPi image versions from S3 if they exist
432
+ *
433
+ * @param string $region
434
+ * @param string $bucket
435
+ * @param array $objects array of image file that could possibly have HDiPi versions
436
+ */
437
+ function maybe_remove_hdipi_images_from_s3( $region, $bucket, $objects ) {
438
+ if ( $objects ) {
439
+ $hidpi_images = array();
440
+ foreach ( $objects as $object ) {
441
+ $hidpi_images[] = array(
442
+ 'Key' => $this->get_hidpi_file_path( $object['Key'] )
443
+ );
444
+ }
445
+
446
+ // Try removing any @2x images but ignore any errors
447
+ $this->delete_s3_objects( $region, $bucket, $hidpi_images );
448
  }
449
+ }
450
 
451
+ /**
452
+ * Removes an attachment's files from S3.
453
+ *
454
+ * @param int $post_id
455
+ * @param array $s3object
456
+ * @param null|string $file optional file override of $s3object['key']
457
+ * @param bool $remove_backup_sizes remove previous edited image versions
458
+ * @param bool $log_error
459
+ * @param bool $return_on_error
460
+ * @param bool $force_new_s3_client if we are deleting in bulk, force new S3 client
461
+ * to cope with possible different regions
462
+ */
463
+ function remove_attachment_files_from_s3( $post_id, $s3object, $file = null, $remove_backup_sizes = true, $log_error = false, $return_on_error = false, $force_new_s3_client = false ) {
464
+ if ( is_null( $file ) ) {
465
+ $file = $s3object['key'];
466
  }
467
 
468
+ $prefix = trailingslashit( dirname( $s3object['key'] ) );
469
+
470
  $bucket = $s3object['bucket'];
471
  $region = $this->get_s3object_region( $s3object );
472
  if ( is_wp_error( $region ) ) {
 
473
  $region = '';
474
  }
475
 
476
+ $objects_to_remove = array();
 
477
 
478
+ $objects_to_remove[] = array(
479
+ 'Key' => path_join( $prefix, basename( $file ) )
480
+ );
481
+ // get any image size files to remove
482
+ $this->prepare_intermediate_images_to_remove( $post_id, $objects_to_remove, $prefix );
483
+ if ( $remove_backup_sizes ) {
484
+ // Remove backup images
485
+ $this->prepare_backup_size_images_to_remove( $post_id, $objects, $prefix );
486
+ }
487
+ // remove any HDiPi iamhges
488
+ $this->maybe_remove_hdipi_images_from_s3( $region, $bucket, $objects_to_remove );
489
 
490
+ // finally delete the objects from S3
491
+ $this->delete_s3_objects( $region, $bucket, $objects_to_remove, $log_error, $return_on_error, $force_new_s3_client );
492
+ }
 
 
 
 
 
493
 
494
+ /**
495
+ * Removes an attachment and intermediate image size files from S3
496
+ *
497
+ * @param int $post_id
498
+ * @param bool $force_new_s3_client if we are deleting in bulk, force new S3 client
499
+ * to cope with possible different regions
500
+ */
501
+ function delete_attachment( $post_id, $force_new_s3_client = false ) {
502
+ if ( ! $this->is_plugin_setup() ) {
503
+ return;
504
  }
505
 
506
+ if ( ! ( $s3object = $this->get_attachment_s3_info( $post_id ) ) ) {
507
+ return;
508
+ }
 
509
 
510
+ $this->remove_attachment_files_from_s3( $post_id, $s3object, null, true, true, true, $force_new_s3_client );
511
 
512
  delete_post_meta( $post_id, 'amazonS3_info' );
513
  }
514
 
515
+ /**
516
+ * Handles the upload of the attachment to S3 when an attachment is updated using
517
+ * the 'wp_update_attachment_metadata' filter
518
+ *
519
+ * @param array $data meta data for attachment
520
+ * @param int $post_id
521
+ *
522
+ * @return array
523
+ */
524
  function wp_update_attachment_metadata( $data, $post_id ) {
525
+ if ( ! $this->is_plugin_setup() ) {
526
+ return $data;
527
+ }
528
+
529
+ if ( ! ( $old_s3object = $this->get_attachment_s3_info( $post_id ) ) && ! $this->get_setting( 'copy-to-s3' ) ) {
530
+ // abort if not already uploaded to S3 and the copy setting is off
531
  return $data;
532
  }
533
 
534
  // allow S3 upload to be cancelled for any reason
535
+ $pre = apply_filters( 'as3cf_pre_update_attachment_metadata', false, $data, $post_id, $old_s3object );
536
  if ( false !== $pre ) {
537
  return $data;
538
  }
546
  /**
547
  * Upload attachment to S3
548
  *
549
+ * @param int $post_id
550
+ * @param array|null $data
551
+ * @param string|null $file_path
552
+ * @param bool $force_new_s3_client if we are uploading in bulk, force new S3 client
553
+ * to cope with possible different regions
554
  *
555
  * @return array|WP_Error $s3object
556
  */
557
+ function upload_attachment_to_s3( $post_id, $data = null, $file_path = null, $force_new_s3_client = false ) {
558
  if ( is_null( $data ) ) {
559
  $data = wp_get_attachment_metadata( $post_id, true );
560
  }
561
 
562
+ if ( is_null( $file_path ) ) {
563
+ $file_path = get_attached_file( $post_id, true );
564
+ }
565
+
566
+ // Check file exists locally before attempting upload
567
+ if ( ! file_exists( $file_path ) ) {
568
+ return new WP_Error( 'exception', sprintf( __( 'File %s does not exist', 'as3cf' ), $file_path ) );
569
+ }
570
+
571
+ $file_name = basename( $file_path );
572
  $type = get_post_mime_type( $post_id );
573
  $allowed_types = $this->get_allowed_mime_types();
574
 
596
  } else {
597
  // derive prefix from various settings
598
  if ( isset( $data['file'] ) ) {
599
+ $time = $this->get_folder_time_from_url( $data['file'] );
600
  } else {
601
  $time = $this->get_attachment_folder_time( $post_id );
602
  $time = date( 'Y/m', $time );
607
  // use bucket from settings
608
  $bucket = $this->get_setting( 'bucket' );
609
  $region = $this->get_setting( 'region' );
610
+ if ( is_wp_error( $region ) ) {
611
+ $region = '';
612
+ }
613
  }
614
 
 
 
 
615
  $acl = apply_filters( 'wps3_upload_acl', $acl, $type, $data, $post_id, $this ); // Old naming convention, will be deprecated soon
616
  $acl = apply_filters( 'as3cf_upload_acl', $acl, $data, $post_id );
617
 
626
  $s3object['acl'] = $acl;
627
  }
628
 
629
+ $s3client = $this->get_s3client( $region, $force_new_s3_client );
630
 
631
+ $args = apply_filters( 'as3cf_object_meta', array(
632
  'Bucket' => $bucket,
633
  'Key' => $prefix . $file_name,
634
  'SourceFile' => $file_path,
635
  'ACL' => $acl,
636
+ ), $post_id );
637
 
638
  // If far future expiration checked (10 years)
639
  if ( $this->get_setting( 'expires' ) ) {
640
  $args['Expires'] = date( 'D, d M Y H:i:s O', time() + 315360000 );
641
  }
642
 
643
+ do_action( 'as3cf_upload_attachment_pre_remove', $post_id, $s3object, $prefix, $args );
 
 
 
 
 
 
 
 
 
 
 
 
644
 
645
  $files_to_remove = array();
646
+
647
  if ( file_exists( $file_path ) ) {
648
  $files_to_remove[] = $file_path;
649
  try {
665
 
666
  if ( isset( $data['thumb'] ) && $data['thumb'] ) {
667
  $path = str_replace( $file_name, $data['thumb'], $file_path );
668
+ if ( file_exists( $path ) && ! in_array( $path, $files_to_remove ) ) {
669
  $additional_images[] = array(
670
  'Key' => $prefix . $data['thumb'],
671
  'SourceFile' => $path,
672
  );
673
+
674
+ $files_to_remove[] = $path;
675
  }
676
  } else if ( ! empty( $data['sizes'] ) ) {
677
  foreach ( $data['sizes'] as $size ) {
678
  $path = str_replace( $file_name, $size['file'], $file_path );
679
+ if ( file_exists( $path ) && ! in_array( $path, $files_to_remove ) ) {
680
  $additional_images[] = array(
681
  'Key' => $prefix . $size['file'],
682
  'SourceFile' => $path,
683
  );
684
+
685
+ $files_to_remove[] = $path;
686
  }
687
  }
688
  }
718
  }
719
 
720
  if ( $this->get_setting( 'remove-local-file' ) ) {
721
+ if ( isset( $_POST['action'] ) && 'image-editor' == sanitize_key( $_POST['action'] ) && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
722
+ // remove original main image after edit
723
+ $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
724
+ $original_file = str_replace( $file_name, basename( $meta['file'] ), $file_path );
725
+ if ( file_exists( $original_file ) && ! in_array( $original_file, $files_to_remove ) ) {
726
+ $files_to_remove[] = $original_file;
727
+ }
728
+ }
729
+
730
  $this->remove_local_files( $files_to_remove );
731
  }
732
 
733
  return $s3object;
734
  }
735
 
736
+ /**
737
+ * Remove files from the local site
738
+ *
739
+ * @param array $file_paths array of files to remove
740
+ */
741
  function remove_local_files( $file_paths ) {
742
  foreach ( $file_paths as $path ) {
743
  if ( ! @unlink( $path ) ) {
768
  return $object_version;
769
  }
770
 
771
+ /**
772
+ * Get the upload folder time from given URL
773
+ *
774
+ * @param string $url
775
+ *
776
+ * @return null|string
777
+ */
778
+ function get_folder_time_from_url( $url ) {
779
+ preg_match( '@[0-9]{4}/[0-9]{2}@', $url, $matches );
780
+
781
+ if ( isset( $matches[0] ) ) {
782
+ return $matches[0];
783
+ }
784
+
785
+ return null;
786
+ }
787
+
788
  // Media files attached to a post use the post's date
789
  // to determine the folder path they are placed in
790
  function get_attachment_folder_time( $post_id ) {
854
 
855
  $bucket = $this->get_setting( 'bucket' );
856
  $region = $this->get_setting( 'region' );
857
+ if ( is_wp_error( $region ) ) {
858
+ return $file;
859
+ }
860
 
861
  $s3client = $this->get_s3client( $region );
862
 
900
  * Generate a link to download a file from Amazon S3 using query string
901
  * authentication. This link is only valid for a limited amount of time.
902
  *
903
+ * @param int $post_id Post ID of the attachment
904
+ * @param int $expires Seconds for the link to live
905
+ * @param string $size Size of the image to get
906
+ * @param array $headers Header overrides for request
907
  *
908
  * @return mixed|void|WP_Error
909
  */
910
+ function get_secure_attachment_url( $post_id, $expires = null, $size = null, $headers = array() ) {
911
  if ( is_null( $expires ) ) {
912
  $expires = self::DEFAULT_EXPIRES;
913
  }
914
+ return $this->get_attachment_url( $post_id, $expires, $size, null, $headers );
915
  }
916
 
917
  /**
918
  * Return the scheme to be used in URLs
919
  *
920
+ * @param string|null $ssl
921
+ *
922
  * @return string
923
  */
924
+ function get_s3_url_scheme( $ssl = null ) {
925
+ if ( $this->use_ssl( $ssl ) ) {
926
  $scheme = 'https';
927
  }
928
  else {
935
  /**
936
  * Determine when to use https in URLS
937
  *
938
+ * @param string|null $ssl
939
+ *
940
  * @return bool
941
  */
942
+ function use_ssl( $ssl = null ) {
943
  $use_ssl = false;
944
 
945
+ if ( is_null( $ssl ) ) {
946
+ $ssl = $this->get_setting( 'ssl' );
947
+ }
948
 
949
  if ( 'request' == $ssl && is_ssl() ) {
950
  $use_ssl = true;
973
  /**
974
  * Get the file prefix
975
  *
976
+ * @param null|string $time
977
+ * @param null|int $post_id
978
  *
979
  * @return string
980
  */
992
  /**
993
  * Get the region specific prefix for S3 URL
994
  *
995
+ * @param string $region
996
+ * @param null|int $expires
997
  *
998
  * @return string
999
  */
1000
+ function get_s3_url_prefix( $region = '', $expires = null ) {
1001
+ $prefix = 's3';
1002
+
1003
+ if ( '' !== $region ) {
1004
+ $delimiter = '-';
1005
+ if ( 'eu-central-1' == $region && ! is_null( $expires ) ) {
1006
+ // if we are creating a secure URL for a Frankfurt base file use the alternative delimiter
1007
+ // http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
1008
+ $delimiter = '.';
1009
+ }
1010
+
1011
+ $prefix .= $delimiter . $region;
1012
+ }
1013
 
1014
  return $prefix;
1015
  }
1017
  /**
1018
  * Get the S3 url for the files
1019
  *
1020
+ * @param string $bucket
1021
  * @param string $region
1022
  * @param int $expires
1023
+ * @param array $args Allows you to specify custom URL settings
1024
  *
1025
  * @return mixed|string|void
1026
  */
1027
+ function get_s3_url_domain( $bucket, $region = '', $expires = null, $args = array() ) {
1028
+ if ( ! isset( $args['cloudfront'] ) ) {
1029
+ $args['cloudfront'] = $this->get_setting( 'cloudfront' );
1030
+ }
1031
 
1032
+ if ( ! isset( $args['domain'] ) ) {
1033
+ $args['domain'] = $this->get_setting( 'domain' );
1034
+ }
1035
+
1036
+ if ( ! isset( $args['ssl'] ) ) {
1037
+ $args['ssl'] = $this->get_setting( 'ssl' );
1038
+ }
1039
+
1040
+ $prefix = $this->get_s3_url_prefix( $region, $expires );
1041
 
1042
+ if ( 'cloudfront' === $args['domain'] && is_null( $expires ) && $args['cloudfront'] ) {
1043
+ $s3_domain = $args['cloudfront'];
1044
  }
1045
+ elseif ( 'virtual-host' === $args['domain'] ) {
1046
  $s3_domain = $bucket;
1047
  }
1048
+ elseif ( 'path' === $args['domain'] || $this->use_ssl( $args['ssl'] ) ) {
1049
  $s3_domain = $prefix . '.amazonaws.com/' . $bucket;
1050
  }
1051
  else {
1058
  /**
1059
  * Get the url of the file from Amazon S3
1060
  *
1061
+ * @param int $post_id Post ID of the attachment
1062
+ * @param int $expires Seconds for the link to live
1063
+ * @param string $size Size of the image to get
1064
+ * @param array $meta Pre retrieved _wp_attachment_metadata for the attachment
1065
+ * @param array $headers Header overrides for request
1066
  *
1067
  * @return bool|mixed|void|WP_Error
1068
  */
1069
+ function get_attachment_url( $post_id, $expires = null, $size = null, $meta = null, $headers = array() ) {
1070
  if ( ! $this->get_setting( 'serve-from-s3' ) ) {
1071
  return false;
1072
  }
1106
  if ( ! is_null( $expires ) ) {
1107
  try {
1108
  $expires = time() + $expires;
1109
+ $secure_url = $this->get_s3client( $region )->getObjectUrl( $s3object['bucket'], $s3object['key'], $expires, $headers );
1110
  }
1111
  catch ( Exception $e ) {
1112
  return new WP_Error( 'exception', $e->getMessage() );
1187
  return $file_path . $file_name;
1188
  }
1189
 
1190
+ /**
1191
+ * Allow processes to update the file on S3 via update_attached_file()
1192
+ *
1193
+ * @param string $file
1194
+ * @param int $attachment_id
1195
+ *
1196
+ * @return string
1197
+ */
1198
+ function update_attached_file( $file, $attachment_id ) {
1199
+ if ( ! $this->is_plugin_setup() ) {
1200
+ return $file;
1201
+ }
1202
+
1203
+ if ( ! ( $s3object = $this->get_attachment_s3_info( $attachment_id ) ) ) {
1204
+ return $file;
1205
+ }
1206
+
1207
+ $file = apply_filters( 'as3cf_update_attached_file', $file, $attachment_id, $s3object );
1208
+
1209
+ return $file;
1210
+ }
1211
+
1212
  /**
1213
  * Return the S3 URL when the local file is missing
1214
  * unless we know the calling process is and we are happy
1215
  * to copy the file back to the server to be used
1216
  *
1217
+ * @param string $file
1218
+ * @param int $attachment_id
1219
  *
1220
+ * @return string
1221
  */
1222
  function get_attached_file( $file, $attachment_id ) {
1223
  if ( file_exists( $file ) || ! $this->get_setting( 'serve-from-s3' ) ) {
1230
 
1231
  $url = $this->get_attachment_url( $attachment_id );
1232
 
1233
+ // return the URL by default
1234
+ $file = apply_filters( 'as3cf_get_attached_file', $url, $file, $attachment_id, $s3object );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1235
 
1236
  return $file;
1237
  }
1238
 
1239
  /**
1240
+ * Helper method for returning data to AJAX call
 
 
 
1241
  *
1242
+ * @param array $return
1243
  */
1244
+ function end_ajax( $return = array() ) {
1245
+ echo json_encode( $return );
1246
+ exit;
 
 
 
 
 
 
 
1247
  }
1248
 
1249
  function verify_ajax_request() {
1258
 
1259
  function ajax_check_bucket() {
1260
  if ( ! isset( $_POST['bucket_name'] ) || ! ( $bucket = sanitize_text_field( $_POST['bucket_name'] ) ) ) { // input var okay
1261
+ $out = array( 'error' => __( 'No bucket name provided.', 'as3cf' ) );
1262
+
1263
+ $this->end_ajax( $out );
1264
  }
1265
 
1266
+ return strtolower( $bucket );
1267
  }
1268
 
1269
+ /**
1270
+ * Handler for AJAX callback to create a bucket in S3
1271
+ */
1272
  function ajax_create_bucket() {
1273
  $this->verify_ajax_request();
1274
 
1275
  $bucket = $this->ajax_check_bucket();
1276
 
1277
+ if ( defined( 'AS3CF_REGION' ) ) {
1278
+ // Are we defining the region?
1279
+ $region = AS3CF_REGION;
1280
  } else {
1281
+ // Are we specifying the region via the form?
1282
+ $region = isset( $_POST['region'] ) ? sanitize_text_field( $_POST['region'] ) : null; // input var okay
 
 
 
 
 
 
 
 
 
 
1283
  }
1284
 
1285
+ $result = $this->create_bucket( $bucket, $region );
1286
+ if ( is_wp_error( $result ) ) {
1287
+ $out = array( 'error' => $result->get_error_message() );
1288
 
1289
+ $this->end_ajax( $out );
 
 
1290
  }
 
 
 
 
 
 
 
 
 
1291
 
1292
+ // check if we were previously selecting a bucket manually via the input
1293
+ $previous_manual_bucket_select = $this->get_setting( 'manual_bucket', false );
1294
 
1295
+ $region = $this->save_bucket( $bucket, $previous_manual_bucket_select, $region );
1296
 
1297
+ if ( ! is_wp_error( $region ) ) {
1298
  $out = array(
1299
  'success' => '1',
1300
+ '_nonce' => wp_create_nonce( 'as3cf-create-bucket' ),
1301
  'region' => $region,
1302
  );
1303
+
1304
+ // Delete transient to force write check
1305
+ delete_site_transient( $this->plugin_prefix . '_bucket_writable' );
1306
+
1307
+ $can_write = $this->check_write_permission( $bucket, $region );
1308
+
1309
+ if ( is_wp_error( $can_write ) ) {
1310
+ $out = array( 'error' => $can_write->get_error_message() );
1311
+ } else {
1312
+ $out['can_write'] = $can_write;
1313
+ }
1314
  } else {
1315
+ $out = array( 'error' => $region->get_error_message() );
1316
  }
1317
 
1318
+ $this->end_ajax( $out );
1319
+ }
1320
+
1321
+ /**
1322
+ * Create an S3 bucket
1323
+ *
1324
+ * @param string $bucket_name
1325
+ * @param null|string $region option location constraint
1326
+ *
1327
+ * @return bool|WP_Error
1328
+ */
1329
+ function create_bucket( $bucket_name, $region = null ) {
1330
+ try {
1331
+ $args = array( 'Bucket' => $bucket_name );
1332
+
1333
+ if ( defined( 'AS3CF_REGION' ) ) {
1334
+ // Make sure we always use the defined region
1335
+ $region = AS3CF_REGION;
1336
+ }
1337
+
1338
+ if ( ! is_null( $region ) && self::DEFAULT_REGION !== $region ) {
1339
+ $args['LocationConstraint'] = $region;
1340
+ }
1341
+
1342
+ $this->get_s3client()->createBucket( $args );
1343
+ }
1344
+ catch ( Exception $e ) {
1345
+ return new WP_Error( 'exception', $e->getMessage() );
1346
+ }
1347
+
1348
+ return true;
1349
+ }
1350
+
1351
+ /**
1352
+ * Handler for AJAX callback to save the selection of a bucket
1353
+ */
1354
+ function ajax_save_bucket() {
1355
+ $this->verify_ajax_request();
1356
+
1357
+ $bucket = $this->ajax_check_bucket();
1358
+
1359
+ $manual = false;
1360
+ // are we inputting the bucket manually?
1361
+ if ( isset( $_POST['action'] ) && false !== strpos( $_POST['action'], 'manual-save-bucket' ) ) {
1362
+ $manual = true;
1363
+ }
1364
+
1365
+ $region = $this->save_bucket( $bucket, $manual );
1366
+
1367
+ if ( ! is_wp_error( $region ) ) {
1368
+ $out = array(
1369
+ 'success' => '1',
1370
+ 'region' => $region,
1371
+ );
1372
+
1373
+ // Delete transient to force write check
1374
+ delete_site_transient( $this->plugin_prefix . '_bucket_writable' );
1375
+
1376
+ $can_write = $this->check_write_permission( $bucket, $region );
1377
+
1378
+ if ( is_wp_error( $can_write ) ) {
1379
+ $out = array( 'error' => $can_write->get_error_message() );
1380
+ } else {
1381
+ $out['can_write'] = $can_write;
1382
+ }
1383
+ } else {
1384
+ $out = array( 'error' => $region->get_error_message() );
1385
+ }
1386
+
1387
+ $this->end_ajax( $out );
1388
+ }
1389
+
1390
+ /**
1391
+ * Perform custom actions before the setting is saved
1392
+ *
1393
+ * @param string $key
1394
+ * @param string $value
1395
+ */
1396
+ function pre_set_setting( $key, $value ) {
1397
+ if ( 'bucket' === $key && ! $this->get_setting( 'bucket' ) ) {
1398
+ // first time bucket select - enable main options by default
1399
+ $this->set_setting( 'copy-to-s3', '1' );
1400
+ $this->set_setting( 'serve-from-s3', '1' );
1401
+ }
1402
  }
1403
 
1404
  /**
1405
  * Save bucket and bucket's region
1406
  *
1407
+ * @param string $bucket_name
1408
+ * @param bool $manual if we are entering the bucket via the manual input form
1409
+ * @param null|string $region
1410
  *
1411
+ * @return string|bool region on success
1412
  */
1413
+ function save_bucket( $bucket_name, $manual = false, $region = null ) {
1414
  if ( $bucket_name ) {
1415
  $this->get_settings();
1416
+
 
 
 
 
1417
  $this->set_setting( 'bucket', $bucket_name );
1418
+
1419
+ if ( is_null( $region ) ) {
1420
+ // retrieve the bucket region if not supplied
1421
+ $region = $this->get_bucket_region( $bucket_name );
1422
+ if ( is_wp_error( $region ) ) {
1423
+ return $region;
1424
+ }
1425
  }
1426
+
1427
  $this->set_setting( 'region', $region );
1428
 
1429
  if ( $manual ) {
1441
  return false;
1442
  }
1443
 
1444
+ /**
1445
+ * Get all AWS regions
1446
+ *
1447
+ * @return array
1448
+ */
1449
+ function get_aws_regions() {
1450
+ $regionEnum = new ReflectionClass( 'Aws\Common\Enum\Region' );
1451
+ $all_regions = $regionEnum->getConstants();
1452
+
1453
+ $regions = array();
1454
+ foreach ( $all_regions as $label => $region ) {
1455
+ // Nicely format region name
1456
+ if ( self::DEFAULT_REGION === $region ) {
1457
+ $label = 'US Standard';
1458
+ } else {
1459
+ $label = strtolower( $label );
1460
+ $label = str_replace( '_', ' ', $label );
1461
+ $label = ucwords( $label );
1462
+ }
1463
 
1464
+ $regions[ $region ] = $label;
 
 
 
 
 
 
 
1465
  }
1466
 
1467
+ return $regions;
 
1468
  }
1469
 
1470
+ /**
1471
+ * Add the settings menu item
1472
+ *
1473
+ * @param Amazon_Web_Services $aws
1474
+ */
1475
  function admin_menu( $aws ) {
1476
+ $this->hook_suffix = $aws->add_page( $this->get_plugin_page_title(), $this->plugin_menu_title, 'manage_options', $this->plugin_slug, array( $this, 'render_page' ) );
1477
+ add_action( 'load-' . $this->hook_suffix , array( $this, 'plugin_load' ) );
1478
  }
1479
 
1480
  /**
1506
  /**
1507
  * Get the region of a bucket
1508
  *
1509
+ * @param string $bucket
1510
  *
1511
  * @return string|WP_Error
1512
  */
1515
  $region = $this->get_s3client()->getBucketLocation( array( 'Bucket' => $bucket ) );
1516
  }
1517
  catch ( Exception $e ) {
1518
+ $error_msg = sprintf( __( 'There was an error attempting to get the region of the bucket %s: %s', 'as3cf' ), $bucket, $e->getMessage() );
1519
+ error_log( $error_msg );
1520
 
1521
  return new WP_Error( 'exception', $e->getMessage() );
1522
  }
1587
  $out = array(
1588
  'success' => '1',
1589
  'buckets' => $result,
 
1590
  );
1591
  }
1592
 
1593
+ $this->end_ajax( $out );
 
1594
  }
1595
 
1596
  /**
1615
  * @param string $bucket
1616
  * @param string $region
1617
  *
1618
+ * @return bool|WP_Error
1619
  */
1620
  function check_write_permission( $bucket = null, $region = null ) {
1621
  if ( is_null( $bucket ) ) {
1625
  }
1626
  }
1627
 
1628
+ $is_bucket_writable = get_site_transient( $this->plugin_prefix . '_bucket_writable' );
1629
+
1630
+ if ( false !== $is_bucket_writable && $is_bucket_writable['bucket'] === $bucket ) {
1631
+ return $is_bucket_writable['can_write'];
1632
+ }
1633
+
1634
  $file_name = 'as3cf-permission-check.txt';
1635
  $file_contents = __( 'This is a test file to check if the user has write permission to S3. Delete me if found.', 'as3cf' );
1636
 
1648
  // need to set region for buckets in non default region
1649
  if ( is_null( $region ) ) {
1650
  $region = $this->get_setting( 'region' );
1651
+
1652
+ if ( is_wp_error( $region ) ) {
1653
+ return $region;
1654
+ }
1655
  }
1656
  // attempt to create the test file
1657
  $this->get_s3client( $region, true )->putObject( $args );
1662
  ) );
1663
  $can_write = true;
1664
  } catch ( Exception $e ) {
1665
+ // if we encounter an error that isn't access denied, throw that error
1666
+ if ( 'AccessDenied' != $e->getExceptionCode() ) {
1667
+ $error_msg = sprintf( __( 'There was an error attempting to check the permissions of the bucket %s: %s', 'as3cf' ), $bucket, $e->getMessage() );
1668
+ error_log( $error_msg );
1669
+
1670
+ return new WP_Error( 'exception', $error_msg );
1671
+ }
1672
+
1673
  // write permission not found
1674
  $can_write = false;
1675
  }
1676
 
1677
+ // Cache the result
1678
+ $is_bucket_writable = array(
1679
+ 'bucket' => $bucket,
1680
+ 'can_write' => $can_write,
1681
+ );
1682
+ set_site_transient( $this->plugin_prefix . '_bucket_writable', $is_bucket_writable, 5 * MINUTE_IN_SECONDS );
1683
+
1684
  return $can_write;
1685
  }
1686
 
1687
+ /**
1688
+ * Render error messages in a view for bucket permission and access issues
1689
+ */
1690
+ function render_bucket_permission_errors() {
1691
+ $can_write = $this->check_write_permission();
1692
+ // catch any checking issues
1693
+ if ( is_wp_error( $can_write ) ) {
1694
+ $this->render_view( 'error-fatal', array( 'message' => $can_write->get_error_message() ) );
1695
+ $can_write = true;
1696
+ }
1697
+ // display a error message if the user does not have write permission to S3 bucket
1698
+ $this->render_view( 'error-access', array( 'can_write' => $can_write ) );
1699
+ }
1700
+
1701
+ /**
1702
+ * Register modal scripts and styles so they can be enqueued later
1703
+ */
1704
+ function register_modal_assets()
1705
+ {
1706
  $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
1707
+ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1708
 
1709
+ $src = plugins_url( 'assets/css/modal.css', $this->plugin_file_path );
1710
+ wp_register_style( 'as3cf-modal', $src, array(), $version );
1711
 
1712
+ $src = plugins_url( 'assets/js/modal' . $suffix . '.js', $this->plugin_file_path );
1713
+ wp_register_script( 'as3cf-modal', $src, array( 'jquery' ), $version, true );
1714
+ }
1715
+
1716
+ function plugin_load() {
1717
+ $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
1718
  $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1719
 
1720
+ $src = plugins_url( 'assets/css/styles.css', $this->plugin_file_path );
1721
+ wp_enqueue_style( 'as3cf-styles', $src, array( 'as3cf-modal' ), $version );
1722
+
1723
  $src = plugins_url( 'assets/js/script' . $suffix . '.js', $this->plugin_file_path );
1724
+ wp_enqueue_script( 'as3cf-script', $src, array( 'jquery', 'as3cf-modal' ), $version, true );
1725
+
1726
+ wp_localize_script( 'as3cf-script',
1727
+ 'as3cf',
1728
+ array(
1729
+ 'strings' => array(
1730
+ 'create_bucket_error' => __( 'Error creating bucket: ', 'as3cf' ),
1731
+ 'create_bucket_name_short' => __( 'Bucket name too short.', 'as3cf' ),
1732
+ 'create_bucket_name_long' => __( 'Bucket name too long.', 'as3cf' ),
1733
+ 'create_bucket_invalid_chars' => __( 'Invalid character. Bucket names can contain lowercase letters, numbers, periods and hyphens.', 'as3cf' ),
1734
+ 'save_bucket_error' => __( 'Error saving bucket: ', 'as3cf' ),
1735
+ 'get_buckets_error' => __( 'Error fetching buckets: ', 'as3cf' ),
1736
+ 'get_url_preview_error' => __( 'Error getting URL preview: ', 'as3cf' ),
1737
+ 'save_alert' => __( 'The changes you made will be lost if you navigate away from this page', 'as3cf' )
1738
+ ),
1739
+ 'nonces' => array(
1740
+ 'create_bucket' => wp_create_nonce( 'as3cf-create-bucket' ),
1741
+ 'manual_bucket' => wp_create_nonce( 'as3cf-manual-save-bucket' ),
1742
+ 'get_buckets' => wp_create_nonce( 'as3cf-get-buckets' ),
1743
+ 'save_bucket' => wp_create_nonce( 'as3cf-save-bucket' ),
1744
+ 'get_url_preview' => wp_create_nonce( 'as3cf-get-url-preview' ),
1745
+ ),
1746
+ 'is_pro' => $this->is_pro(),
1747
+ )
1748
+ );
1749
 
1750
  $this->handle_post_request();
1751
+ $this->http_prepare_download_log();
1752
+
1753
+ do_action( 'as3cf_plugin_load' );
1754
  }
1755
 
1756
+ /**
1757
+ * Whitelist of settings allowed to be saved
1758
+ *
1759
+ * @return array
1760
+ */
1761
+ function get_settings_whitelist() {
1762
+ return array(
1763
+ 'bucket',
1764
+ 'region',
1765
+ 'domain',
1766
+ 'virtual-host',
1767
+ 'expires',
1768
+ 'permissions',
1769
+ 'cloudfront',
1770
+ 'object-prefix',
1771
+ 'copy-to-s3',
1772
+ 'serve-from-s3',
1773
+ 'remove-local-file',
1774
+ 'ssl',
1775
+ 'hidpi-images',
1776
+ 'object-versioning',
1777
+ 'use-yearmonth-folders',
1778
+ 'enable-object-prefix',
1779
+ );
1780
+ }
1781
+
1782
+ /**
1783
+ * Handle the saving of the settings page
1784
+ */
1785
  function handle_post_request() {
1786
+ if ( empty( $_POST['plugin'] ) || $this->get_plugin_slug() != sanitize_key( $_POST['plugin'] ) ) { // input var okay
1787
+ return;
1788
+ }
1789
+
1790
  if ( empty( $_POST['action'] ) || 'save' != sanitize_key( $_POST['action'] ) ) { // input var okay
1791
  return;
1792
  }
1793
 
1794
+ if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), $this->get_settings_nonce_key() ) ) { // input var okay
1795
+ die( __( "Cheatin' eh?", 'as3cf' ) );
1796
  }
1797
 
1798
+ do_action( 'as3cf_pre_save_settings' );
1799
+
1800
+ $post_vars = $this->get_settings_whitelist();
1801
 
1802
  foreach ( $post_vars as $var ) {
1803
  $this->remove_setting( $var );
1813
 
1814
  $this->save_settings();
1815
 
1816
+ $url = $this->get_plugin_page_url( array( 'updated' => '1' ) );
1817
+ wp_redirect( $url );
1818
  exit;
1819
  }
1820
 
1821
+ /**
1822
+ * Helper method to return the settings page URL for the plugin
1823
+ *
1824
+ * @param array $args
1825
+ * @param string $url_method To prepend to admin_url()
1826
+ * @param bool $escape Should we escape the URL
1827
+ *
1828
+ * @return string
1829
+ */
1830
+ function get_plugin_page_url( $args = array(), $url_method = 'network', $escape = true ) {
1831
+ $default_args = array(
1832
+ 'page' => self::$plugin_page,
1833
+ );
1834
+
1835
+ $args = array_merge( $args, $default_args );
1836
+
1837
+ switch ( $url_method ) {
1838
+ case 'self':
1839
+ $base_url = self_admin_url( 'admin.php' );
1840
+ break;
1841
+ case '':
1842
+ $base_url = admin_url( 'admin.php' );
1843
+ break;
1844
+ default:
1845
+ $base_url = network_admin_url( 'admin.php' );
1846
+ }
1847
+
1848
+ // Add a hash to the URL
1849
+ $hash = false;
1850
+ if ( isset( $args['hash'] ) ) {
1851
+ $hash = $args['hash'];
1852
+ unset( $args['hash'] );
1853
+ } else if ( $this->default_tab ) {
1854
+ $hash = $this->default_tab;
1855
+ }
1856
+
1857
+ $url = add_query_arg( $args, $base_url );
1858
+
1859
+ if ( $hash ) {
1860
+ $url .= '#' . $hash;
1861
+ }
1862
+
1863
+ if ( $escape ) {
1864
+ $url = esc_url_raw( $url );
1865
+ }
1866
+
1867
+ return $url;
1868
+ }
1869
+
1870
  /**
1871
  * Display the main settings page for the plugin
1872
  */
1873
  function render_page() {
1874
+ $this->aws->render_view( 'header', array( 'page_title' => $this->get_plugin_page_title(), 'page' => 'as3cf' ) );
1875
 
1876
  $aws_client = $this->aws->get_client();
1877
 
1879
  $this->render_view( 'error-fatal', array( 'message' => $aws_client->get_error_message() ) );
1880
  }
1881
  else {
1882
+ $this->render_view( 'settings-tabs' );
1883
  do_action( 'as3cf_pre_settings_render' );
1884
  $this->render_view( 'settings' );
1885
  do_action( 'as3cf_post_settings_render' );
1888
  $this->aws->render_view( 'footer' );
1889
  }
1890
 
1891
+ /**
1892
+ * Get the tabs available for the plugin settings page
1893
+ *
1894
+ * @return array
1895
+ */
1896
+ function get_settings_tabs() {
1897
+ $tabs = array(
1898
+ 'media' => _x( 'Media Library', 'Show the media library tab', 'as3cf' ),
1899
+ 'support' => _x( 'Support', 'Show the support tab', 'as3cf' )
1900
+ );
1901
+
1902
+ return apply_filters( 'as3cf_settings_tabs', $tabs );
1903
+ }
1904
+
1905
  /**
1906
  * Get the prefix path for the files
1907
  *
1910
  * @return string
1911
  */
1912
  function get_dynamic_prefix( $time = null ) {
 
1913
  $prefix = '';
1914
  if ( $this->get_setting( 'use-yearmonth-folders' ) ) {
1915
+ $uploads = wp_upload_dir( $time );
1916
+ $prefix = str_replace( $this->get_base_upload_path(), '', $uploads['path'] );
1917
  }
1918
 
1919
  // support legacy MS installs (<3.5 since upgraded) for subsites
1920
+ if ( is_multisite() && ! ( is_main_network() && is_main_site() ) && false === strpos( $prefix, 'sites/' ) ) {
1921
+ $details = get_blog_details( get_current_blog_id() );
1922
+ $legacy_ms_prefix = 'sites/' . $details->blog_id . '/';
 
1923
  $legacy_ms_prefix = apply_filters( 'as3cf_legacy_ms_subsite_prefix', $legacy_ms_prefix, $details );
1924
  $prefix = '/' . trailingslashit( ltrim( $legacy_ms_prefix, '/' ) ) . ltrim( $prefix, '/' );
1925
  }
1966
 
1967
  $blog_ids = array();
1968
  foreach ( $blogs as $blog ) {
 
 
 
 
1969
  $blog_ids[] = $blog['blog_id'];
1970
  }
1971
 
1972
  return $blog_ids;
1973
  }
1974
+
1975
+ /**
1976
+ * Check whether the pro addon is installed.
1977
+ *
1978
+ * @return bool
1979
+ */
1980
+ function is_pro() {
1981
+ if ( ! class_exists( 'Amazon_S3_And_CloudFront_Pro' ) ) {
1982
+ return false;
1983
+ }
1984
+
1985
+ return true;
1986
+ }
1987
+
1988
+ /**
1989
+ * Apply ACL to an attachment and associated files
1990
+ *
1991
+ * @param int $post_id
1992
+ * @param object $s3object
1993
+ * @param string $acl
1994
+ */
1995
+ function set_attachment_acl_on_s3( $post_id, $s3object, $acl ) {
1996
+ // set ACL as private
1997
+ $args = array(
1998
+ 'ACL' => $acl,
1999
+ 'Bucket' => $s3object['bucket'],
2000
+ 'Key' => $s3object['key'],
2001
+ );
2002
+
2003
+ $region = ( isset( $s3object['region'] ) ) ? $s3object['region'] : false;
2004
+ $s3client = $this->get_s3client( $region, true );
2005
+
2006
+ try {
2007
+ $s3client->PutObjectAcl( $args );
2008
+ $s3object['acl'] = $acl;
2009
+
2010
+ // Add attachment to ACL update notice
2011
+ $message = $this->make_acl_admin_notice_text( $s3object );
2012
+ $this->set_admin_notice( $message );
2013
+
2014
+ // update S3 meta data
2015
+ if ( $acl == self::DEFAULT_ACL ) {
2016
+ unset( $s3object['acl'] );
2017
+ }
2018
+ update_post_meta( $post_id, 'amazonS3_info', $s3object );
2019
+ } catch ( Exception $e ) {
2020
+ error_log( 'Error setting ACL to ' . $acl . ' for ' . $s3object['key'] . ': ' . $e->getMessage() );
2021
+ }
2022
+ }
2023
+
2024
+ /**
2025
+ * Make admin notice text for when object ACL has changed
2026
+ *
2027
+ * @param array $s3object
2028
+ *
2029
+ * @return string
2030
+ */
2031
+ function make_acl_admin_notice_text( $s3object ) {
2032
+ $filename = basename( $s3object['key'] );
2033
+ $acl = $this->get_acl_display_name( $s3object['acl'] );
2034
+
2035
+ return sprintf( __( 'The file %s has been given %s permissions on Amazon S3.', 'as3cf' ), "<strong>{$filename}</strong>", "<strong>{$acl}</strong>" );
2036
+ }
2037
+
2038
+ /**
2039
+ * Set admin notice
2040
+ *
2041
+ * @param string $message
2042
+ * @param string $type info, updated, error
2043
+ * @param bool $dismissible
2044
+ * @param bool $inline
2045
+ */
2046
+ function set_admin_notice( $message, $type = 'info', $dismissible = true, $inline = false ) {
2047
+ self::$admin_notices[] = array(
2048
+ 'message' => $message,
2049
+ 'type' => $type,
2050
+ 'dismissible' => $dismissible,
2051
+ 'inline' => $inline,
2052
+ );
2053
+ }
2054
+
2055
+ /**
2056
+ * Save admin notices to transients before shutdown
2057
+ */
2058
+ function save_admin_notices() {
2059
+ if ( ! empty( self::$admin_notices ) ) {
2060
+ set_site_transient( 'as3cf_notices', self::$admin_notices );
2061
+ }
2062
+ }
2063
+
2064
+ /**
2065
+ * Maybe show notices on admin page
2066
+ */
2067
+ function maybe_show_admin_notices() {
2068
+ if ( $notices = get_site_transient( 'as3cf_notices' ) ) {
2069
+ foreach ( $notices as $notice ) {
2070
+ if ( 'info' === $notice['type'] ) {
2071
+ $notice['type'] = 'notice-info';
2072
+ }
2073
+
2074
+ $args = array(
2075
+ 'message' => $notice['message'],
2076
+ 'type' => $notice['type'],
2077
+ 'dismissible' => $notice['dismissible'],
2078
+ 'inline' => $notice['inline'],
2079
+ );
2080
+
2081
+ $this->render_view( 'notice', $args );
2082
+ }
2083
+
2084
+ delete_site_transient( 'as3cf_notices' );
2085
+ }
2086
+ }
2087
+
2088
+ /**
2089
+ * Diagnostic information for the support tab
2090
+ *
2091
+ * @param bool $escape
2092
+ */
2093
+ function output_diagnostic_info( $escape = true ) {
2094
+ global $table_prefix;
2095
+ global $wpdb;
2096
+
2097
+ echo 'site_url(): ';
2098
+ echo esc_html( site_url() );
2099
+ echo "\r\n";
2100
+
2101
+ echo 'home_url(): ';
2102
+ echo esc_html( home_url() );
2103
+ echo "\r\n";
2104
+
2105
+ echo 'Database Name: ';
2106
+ echo esc_html( $wpdb->dbname );
2107
+ echo "\r\n";
2108
+
2109
+ echo 'Table Prefix: ';
2110
+ echo esc_html( $table_prefix );
2111
+ echo "\r\n";
2112
+
2113
+ echo 'WordPress: ';
2114
+ echo bloginfo( 'version' );
2115
+ if ( is_multisite() ) {
2116
+ echo ' Multisite';
2117
+ }
2118
+ echo "\r\n";
2119
+
2120
+ echo 'Web Server: ';
2121
+ echo esc_html( ! empty( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '' );
2122
+ echo "\r\n";
2123
+
2124
+ echo 'PHP: ';
2125
+ if ( function_exists( 'phpversion' ) ) {
2126
+ echo esc_html( phpversion() );
2127
+ }
2128
+ echo "\r\n";
2129
+
2130
+ echo 'MySQL: ';
2131
+ echo esc_html( empty( $wpdb->use_mysqli ) ? mysql_get_server_info() : mysqli_get_server_info( $wpdb->dbh ) );
2132
+ echo "\r\n";
2133
+
2134
+ echo 'ext/mysqli: ';
2135
+ echo empty( $wpdb->use_mysqli ) ? 'no' : 'yes';
2136
+ echo "\r\n";
2137
+
2138
+ echo 'WP Memory Limit: ';
2139
+ echo esc_html( WP_MEMORY_LIMIT );
2140
+ echo "\r\n";
2141
+
2142
+ echo 'Blocked External HTTP Requests: ';
2143
+ if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) {
2144
+ echo 'None';
2145
+ } else {
2146
+ $accessible_hosts = ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) ? WP_ACCESSIBLE_HOSTS : '';
2147
+
2148
+ if ( empty( $accessible_hosts ) ) {
2149
+ echo 'ALL';
2150
+ } else {
2151
+ echo 'Partially (Accessible Hosts: ' . esc_html( $accessible_hosts ) . ')';
2152
+ }
2153
+ }
2154
+ echo "\r\n";
2155
+
2156
+ echo 'WP Locale: ';
2157
+ echo esc_html( get_locale() );
2158
+ echo "\r\n";
2159
+
2160
+ echo 'Debug Mode: ';
2161
+ echo esc_html( ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? 'Yes' : 'No' );
2162
+ echo "\r\n";
2163
+
2164
+ echo 'WP Max Upload Size: ';
2165
+ echo esc_html( size_format( wp_max_upload_size() ) );
2166
+ echo "\r\n";
2167
+
2168
+ echo 'PHP Time Limit: ';
2169
+ if ( function_exists( 'ini_get' ) ) {
2170
+ echo esc_html( ini_get( 'max_execution_time' ) );
2171
+ }
2172
+ echo "\r\n";
2173
+
2174
+ echo 'PHP Error Log: ';
2175
+ if ( function_exists( 'ini_get' ) ) {
2176
+ echo esc_html( ini_get( 'error_log' ) );
2177
+ }
2178
+ echo "\r\n";
2179
+
2180
+ echo 'fsockopen: ';
2181
+ if ( function_exists( 'fsockopen' ) ) {
2182
+ echo 'Enabled';
2183
+ } else {
2184
+ echo 'Disabled';
2185
+ }
2186
+ echo "\r\n";
2187
+
2188
+ echo 'OpenSSL: ';
2189
+ if ( $this->open_ssl_enabled() ) {
2190
+ echo esc_html( OPENSSL_VERSION_TEXT );
2191
+ } else {
2192
+ echo 'Disabled';
2193
+ }
2194
+ echo "\r\n";
2195
+
2196
+ echo 'cURL: ';
2197
+ if ( function_exists( 'curl_init' ) ) {
2198
+ echo 'Enabled';
2199
+ } else {
2200
+ echo 'Disabled';
2201
+ }
2202
+ echo "\r\n";
2203
+
2204
+ echo 'Zlib Compression: ';
2205
+ if ( function_exists( 'gzcompress' ) ) {
2206
+ echo 'Enabled';
2207
+ } else {
2208
+ echo 'Disabled';
2209
+ }
2210
+ echo "\r\n\r\n";
2211
+
2212
+ echo 'Bucket: ';
2213
+ echo $this->get_setting( 'bucket' );
2214
+ echo "\r\n";
2215
+ echo 'Region: ';
2216
+ $region = $this->get_setting( 'region' );
2217
+ if ( ! is_wp_error( $region ) ) {
2218
+ echo $region;
2219
+ }
2220
+ echo "\r\n";
2221
+ echo 'Copy Files to S3: ';
2222
+ echo $this->on_off( 'copy-to-s3' );
2223
+ echo "\r\n";
2224
+ echo 'Rewrite File URLs: ';
2225
+ echo $this->on_off( 'serve-from-s3' );
2226
+ echo "\r\n";
2227
+ echo "\r\n";
2228
+
2229
+ echo 'URL Preview: ';
2230
+ echo $this->get_url_preview( $escape );
2231
+ echo "\r\n";
2232
+ echo "\r\n";
2233
+
2234
+ echo 'Domain: ';
2235
+ echo $this->get_setting( 'domain' );
2236
+ echo "\r\n";
2237
+ echo 'Enable Path: ';
2238
+ echo $this->on_off( 'enable-object-prefix' );
2239
+ echo "\r\n";
2240
+ echo 'Custom Path: ';
2241
+ echo $this->get_setting( 'object-prefix' );
2242
+ echo "\r\n";
2243
+ echo 'Use Year/Month: ';
2244
+ echo $this->on_off( 'use-yearmonth-folders' );
2245
+ echo "\r\n";
2246
+ echo 'SSL: ';
2247
+ echo $this->get_setting( 'ssl' );
2248
+ echo "\r\n";
2249
+ echo 'Remove Files From Server: ';
2250
+ echo $this->on_off( 'remove-local-file' );
2251
+ echo "\r\n";
2252
+ echo 'Object Versioning: ';
2253
+ echo $this->on_off( 'object-versioning' );
2254
+ echo "\r\n";
2255
+ echo 'Far Future Expiration Header: ';
2256
+ echo $this->on_off( 'expires' );
2257
+ echo "\r\n";
2258
+ echo 'Copy HiDPI (@2x) Images: ';
2259
+ echo $this->on_off( 'hidpi-images' );
2260
+ echo "\r\n\r\n";
2261
+
2262
+ do_action( 'as3cf_diagnostic_info' );
2263
+ if ( has_action( 'as3cf_diagnostic_info' ) ) {
2264
+ echo "\r\n";
2265
+ }
2266
+
2267
+ echo "Active Plugins:\r\n";
2268
+ $active_plugins = (array) get_option( 'active_plugins', array() );
2269
+
2270
+ if ( is_multisite() ) {
2271
+ $network_active_plugins = wp_get_active_network_plugins();
2272
+ $active_plugins = array_map( array( $this, 'remove_wp_plugin_dir' ), $network_active_plugins );
2273
+ }
2274
+
2275
+ foreach ( $active_plugins as $plugin ) {
2276
+ $this->print_plugin_details( WP_PLUGIN_DIR . '/' . $plugin );
2277
+ }
2278
+ }
2279
+
2280
+ /**
2281
+ * Helper for displaying settings
2282
+ *
2283
+ * @param $key setting key
2284
+ *
2285
+ * @return string
2286
+ */
2287
+ function on_off( $key ) {
2288
+ $value = $this->get_setting( $key, 0 );
2289
+
2290
+ return ( 1 == $value ) ? 'On' : 'Off';
2291
+ }
2292
+
2293
+ /**
2294
+ * Helper to display plugin details
2295
+ *
2296
+ * @param $plugin_path
2297
+ * @param string $suffix
2298
+ */
2299
+ function print_plugin_details( $plugin_path, $suffix = '' ) {
2300
+ $plugin_data = get_plugin_data( $plugin_path );
2301
+ if ( empty( $plugin_data['Name'] ) ) {
2302
+ return;
2303
+ }
2304
+
2305
+ printf( "%s%s (v%s) by %s\r\n", $plugin_data['Name'], $suffix, $plugin_data['Version'], strip_tags( $plugin_data['AuthorName'] ) );
2306
+ }
2307
+
2308
+ /**
2309
+ * Helper to remove the plugin directory from the plugin path
2310
+ *
2311
+ * @param string $path
2312
+ *
2313
+ * @return string
2314
+ */
2315
+ function remove_wp_plugin_dir( $path ) {
2316
+ $plugin = str_replace( WP_PLUGIN_DIR, '', $path );
2317
+
2318
+ return substr( $plugin, 1 );
2319
+ }
2320
+
2321
+ /**
2322
+ * Check for as3cf-download-log and related nonce and if found begin the
2323
+ * download of the diagnostic log
2324
+ *
2325
+ * @return void
2326
+ */
2327
+ function http_prepare_download_log() {
2328
+ if ( isset( $_GET['as3cf-download-log'] ) && wp_verify_nonce( $_GET['nonce'], 'as3cf-download-log' ) ) {
2329
+ ob_start();
2330
+ $this->output_diagnostic_info( false );
2331
+ $log = ob_get_clean();
2332
+ $url = parse_url( home_url() );
2333
+ $host = sanitize_file_name( $url['host'] );
2334
+ $filename = sprintf( '%s-diagnostic-log-%s.txt', $host, date( 'YmdHis' ) );
2335
+ header( 'Content-Description: File Transfer' );
2336
+ header( 'Content-Type: application/octet-stream' );
2337
+ header( 'Content-Length: ' . strlen( $log ) );
2338
+ header( 'Content-Disposition: attachment; filename=' . $filename );
2339
+ echo $log;
2340
+ exit;
2341
+ }
2342
+ }
2343
+
2344
+ /**
2345
+ * Return human friendly ACL name
2346
+ *
2347
+ * @param string $acl
2348
+ *
2349
+ * @return string
2350
+ */
2351
+ function get_acl_display_name( $acl ) {
2352
+ return ucwords( str_replace( '-', ' ', $acl ) );
2353
+ }
2354
+
2355
+ /**
2356
+ * Detect if OpenSSL is enabled
2357
+ *
2358
+ * @return bool
2359
+ */
2360
+ function open_ssl_enabled() {
2361
+ if ( defined( 'OPENSSL_VERSION_TEXT' ) ) {
2362
+ return true;
2363
+ } else {
2364
+ return false;
2365
+ }
2366
+ }
2367
+
2368
+ /**
2369
+ * Is the current blog ID that specified in wp-config.php
2370
+ *
2371
+ * @param int $blog_id
2372
+ *
2373
+ * @return bool
2374
+ */
2375
+ function is_current_blog( $blog_id ) {
2376
+ $default = defined( 'BLOG_ID_CURRENT_SITE' ) ? BLOG_ID_CURRENT_SITE : 1;
2377
+
2378
+ if ( $default === $blog_id ) {
2379
+ return true;
2380
+ }
2381
+
2382
+ return false;
2383
+ }
2384
  }
classes/as3cf-compatibility-check.php DELETED
@@ -1,105 +0,0 @@
1
- <?php
2
- class AS3CF_Compatibility_Check {
3
-
4
- private $plugin_file_path, $aws_plugin_version_required;
5
-
6
- function __construct( $plugin_file_path, $aws_plugin_version_required ) {
7
- $this->plugin_file_path = $plugin_file_path;
8
- $this->aws_plugin_version_required = $aws_plugin_version_required;
9
-
10
- add_action( 'admin_notices', array( $this, 'hook_admin_notices' ) );
11
- add_action( 'network_admin_notices', array( $this, 'hook_admin_notices' ) );
12
- }
13
-
14
- function is_compatible() {
15
- return $this->get_error_msg() ? false : true;
16
- }
17
-
18
- function get_error_msg() {
19
- static $msg;
20
-
21
- if ( ! is_null( $msg ) ) {
22
- return $msg;
23
- }
24
-
25
- $hide_notice_msg = '<br>' . __( 'You can deactivate the Amazon S3 and CloudFront plugin to get rid of this notice.', 'as3cf' );
26
-
27
- if ( ! class_exists( 'Amazon_Web_Services' ) ) {
28
- $msg = sprintf( __( 'Amazon S3 and CloudFront has been disabled as it requires the <a style="text-decoration:none;" href="%s">Amazon&nbsp;Web&nbsp;Services</a> plugin.', 'as3cf' ), 'http://wordpress.org/extend/plugins/amazon-web-services/' );
29
-
30
- if ( file_exists( WP_PLUGIN_DIR . '/amazon-web-services/amazon-web-services.php' ) ) {
31
- $msg .= ' ' . __( 'It appears to be installed already.', 'as3cf' );
32
- $activate_url = wp_nonce_url( network_admin_url( 'plugins.php?action=activate&amp;plugin=amazon-web-services/amazon-web-services.php' ), 'activate-plugin_amazon-web-services/amazon-web-services.php' );
33
- $msg .= ' <a style="font-weight:bold;text-decoration:none;" href="' . $activate_url . '">' . _x( 'Activate it now', 'Activate plugin', 'as3cf' ) . '</a>';
34
- }
35
- else {
36
- $install_url = wp_nonce_url( network_admin_url( 'update.php?action=install-plugin&plugin=amazon-web-services' ), 'install-plugin_amazon-web-services' );
37
- $msg .= ' ' . sprintf( __( '<a href="%s">Install it</a> and activate.', 'as3cf' ), $install_url );
38
- }
39
-
40
- $msg .= $hide_notice_msg;
41
-
42
- return $msg;
43
- }
44
-
45
- $aws_plugin_version = isset( $GLOBALS['aws_meta']['amazon-web-services']['version'] ) ? $GLOBALS['aws_meta']['amazon-web-services']['version'] : 0;
46
-
47
- if ( ! version_compare( $aws_plugin_version, $this->aws_plugin_version_required, '>=' ) ) {
48
- $msg = sprintf( __( 'Amazon S3 and CloudFront has been disabled as it requires version %s or later of the <a style="text-decoration:none;" href="%s">Amazon&nbsp;Web&nbsp;Services</a> plugin.', 'as3cf' ), $this->aws_plugin_version_required, 'http://wordpress.org/extend/plugins/amazon-web-services/' );
49
-
50
- if ( $aws_plugin_version ) {
51
- $msg .= ' ' . sprintf( __( 'You currently have version %s installed.', 'as3cf' ), $aws_plugin_version );
52
- }
53
-
54
- $update_url = wp_nonce_url( network_admin_url( 'update.php?action=upgrade-plugin&plugin=amazon-web-services/amazon-web-services.php' ), 'upgrade-plugin_amazon-web-services/amazon-web-services.php' );
55
- $msg .= ' <a style="font-weight:bold;text-decoration:none;white-space:nowrap;" href="' . $update_url . '">' . __( 'Update to the latest version', 'as3cf' ) . '</a>';
56
-
57
- $msg .= $hide_notice_msg;
58
-
59
- return $msg;
60
- }
61
-
62
- $as3cf_plugin_version_required = $GLOBALS['aws_meta']['amazon-web-services']['supported_addon_versions']['amazon-s3-and-cloudfront'];
63
- $as3cf_plugin_version = $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'];
64
-
65
- if ( ! version_compare( $as3cf_plugin_version, $as3cf_plugin_version_required, '>=' ) ) {
66
- $msg = sprintf( __( 'Amazon S3 and CloudFront has been disabled because it will not work with the version of the Amazon&nbsp;Web&nbsp;Services plugin installed. Amazon&nbsp;S3&nbsp;and&nbsp;CloudFront %s or later is required.', 'as3cf' ), $as3cf_plugin_version_required );
67
-
68
- $plugin_basename = plugin_basename( __FILE__ );
69
- $update_url = wp_nonce_url( network_admin_url( 'update.php?action=upgrade-plugin&plugin=' . $plugin_basename ), 'upgrade-plugin_' . $plugin_basename );
70
- $msg .= ' <a style="font-weight:bold;text-decoration:none;white-space:nowrap;" href="' . $update_url . '">' . __( 'Update Amazon S3 and CloudFront to the latest version', 'as3cf' ) . '</a>';
71
-
72
- $msg .= $hide_notice_msg;
73
-
74
- return $msg;
75
- }
76
-
77
- $msg = false;
78
- return $msg;
79
- }
80
-
81
- function hook_admin_notices() {
82
- if ( is_multisite() ) {
83
- if ( ! current_user_can( 'manage_network_plugins' ) ) {
84
- return; // Don't show notices if the user can't manage network plugins
85
- }
86
- }
87
- else {
88
- // Don't show notices if user doesn't have plugin management privileges
89
- $caps = array( 'activate_plugins', 'update_plugins', 'install_plugins' );
90
- foreach ( $caps as $cap ) {
91
- if ( ! current_user_can( $cap ) ) {
92
- return;
93
- }
94
- }
95
- }
96
-
97
- $error_msg = $this->get_error_msg();
98
-
99
- if ( ! $error_msg ) {
100
- return;
101
- }
102
-
103
- printf( '<div class="error"><p>%s</p></div>', $error_msg );
104
- }
105
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/as3cf-plugin-compatibility.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Compatibility
4
+ *
5
+ * @package amazon-s3-and-cloudfront
6
+ * @subpackage Classes/Plugin-Compatibility
7
+ * @copyright Copyright (c) 2015, Delicious Brains
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.8.3
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * AS3CF_Plugin_Compatibility Class
19
+ *
20
+ * This class handles compatibility code for third party plugins used in conjunction with AS3CF
21
+ *
22
+ * @since 0.8.3
23
+ */
24
+ class AS3CF_Plugin_Compatibility {
25
+
26
+ /**
27
+ * @var Amazon_S3_And_CloudFront
28
+ */
29
+ protected $as3cf;
30
+
31
+ function __construct( $as3cf ) {
32
+ $this->as3cf = $as3cf;
33
+
34
+ $this->compatibility_init();
35
+ }
36
+
37
+ /**
38
+ * Register the compatibility hooks
39
+ */
40
+ function compatibility_init() {
41
+ /*
42
+ * WP_Image_Editor
43
+ * /wp-includes/class-wp-image-editor.php
44
+ */
45
+ add_action( 'as3cf_upload_attachment_pre_remove', array( $this, 'image_editor_remove_files' ), 10, 4 );
46
+ add_filter( 'as3cf_get_attached_file', array( $this, 'image_editor_download_file' ), 10, 4 );
47
+
48
+ /*
49
+ * WP_Customize_Control
50
+ * /wp-includes/class-wp-customize_control.php
51
+ */
52
+ add_filter( 'attachment_url_to_postid', array( $this, 'customizer_background_image' ), 10, 2 );
53
+
54
+ /*
55
+ * Regenerate Thumbnails
56
+ * https://wordpress.org/plugins/regenerate-thumbnails/
57
+ */
58
+ add_filter( 'as3cf_get_attached_file', array( $this, 'regenerate_thumbnails_download_file' ), 10, 4 );
59
+ }
60
+
61
+ /**
62
+ * Allow the WordPress Image Editor to remove edited version of images
63
+ * if the original image is being restored and 'IMAGE_EDIT_OVERWRITE' is set
64
+ *
65
+ * @param int $post_id
66
+ * @param array $s3object
67
+ * @param string $prefix
68
+ * @param array $args
69
+ */
70
+ function image_editor_remove_files( $post_id, $s3object, $prefix, $args ) {
71
+ if ( isset( $_POST['do'] ) && 'restore' == $_POST['do'] && defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
72
+ $meta = get_post_meta( $post_id, '_wp_attachment_metadata', true );
73
+ $this->as3cf->remove_attachment_files_from_s3( $post_id, $s3object, $meta['file'] );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Allow the WordPress Image Editor to edit files that have been copied to S3
79
+ * but removed from the local server, by copying them back temporarily
80
+ *
81
+ * @param string $url
82
+ * @param string $file
83
+ * @param int $attachment_id
84
+ * @param array $s3_object
85
+ *
86
+ * @return string
87
+ */
88
+ function image_editor_download_file( $url, $file, $attachment_id, $s3_object ) {
89
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
90
+ return $url;
91
+ }
92
+
93
+ // When the image-editor restores the original it requests the edited image,
94
+ // but we actually need to copy back the original image at this point
95
+ // for the restore to be successful and edited images to be deleted from S3
96
+ // via image_editor_remove_files()
97
+ if ( isset( $_POST['do'] ) && 'restore' == $_POST['do'] ) {
98
+ $backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
99
+ $original_filename = $backup_sizes['full-orig']['file'];
100
+
101
+ $orig_s3 = $s3_object;
102
+ $orig_s3['key'] = dirname( $s3_object['key'] ) . '/' . $original_filename;
103
+ $orig_file = dirname( $file ) . '/' . $original_filename;
104
+
105
+ // Copy the original file back to the server for the restore process
106
+ $this->copy_s3_file_to_server( $orig_s3, $orig_file );
107
+
108
+ // Copy the edited file back to the server as well, it will be cleaned up later
109
+ if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
110
+ // Return the file if successfully downloaded from S3
111
+ return $file;
112
+ };
113
+ }
114
+
115
+ // must be the image-editor process
116
+ if ( isset( $_POST['action'] ) && 'image-editor' == sanitize_key( $_POST['action'] ) ) { // input var okay
117
+ $callers = debug_backtrace();
118
+ foreach ( $callers as $caller ) {
119
+ if ( isset( $caller['function'] ) && '_load_image_to_edit_path' == $caller['function'] ) {
120
+ // check this has been called by '_load_image_to_edit_path' so as only to copy back once
121
+ if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
122
+ // Return the file if successfully downloaded from S3
123
+ return $file;
124
+ };
125
+ }
126
+ }
127
+ }
128
+
129
+ return $url;
130
+ }
131
+
132
+ /**
133
+ * Show the correct background image in the customizer
134
+ *
135
+ * @param int|null $post_id
136
+ * @param string $url
137
+ *
138
+ * @return int|null
139
+ */
140
+ function customizer_background_image( $post_id, $url ) {
141
+ if ( ! is_null( $post_id ) ) {
142
+ return $post_id;
143
+ }
144
+ $url = parse_url( $url );
145
+
146
+ if ( ! isset( $url['path'] ) ) {
147
+ return $post_id; // URL path can't be determined
148
+ }
149
+
150
+ $key1 = ltrim( $url['path'], '/' );
151
+ $length1 = strlen( $key1 );
152
+
153
+ // URLs may contain the bucket name within the path, therefore we must
154
+ // also perform the search with the first path segment removed
155
+ $parts = explode( '/', $key1 );
156
+ unset( $parts[0] );
157
+
158
+ $key2 = implode( '/', $parts );
159
+ $length2 = strlen( $key2 );
160
+
161
+ global $wpdb;
162
+ $sql = "
163
+ SELECT `post_id`
164
+ FROM `{$wpdb->prefix}postmeta`
165
+ WHERE `{$wpdb->prefix}postmeta`.`meta_key` = 'amazonS3_info'
166
+ AND ( `{$wpdb->prefix}postmeta`.`meta_value` LIKE '%s:3:\"key\";s:{$length1}:\"{$key1}\";%'
167
+ OR `{$wpdb->prefix}postmeta`.`meta_value` LIKE '%s:3:\"key\";s:{$length2}:\"{$key2}\";%' )
168
+ ";
169
+
170
+ if ( $id = $wpdb->get_var( $sql ) ) {
171
+ return $id;
172
+ }
173
+
174
+ return $post_id; // No attachment found on S3
175
+ }
176
+
177
+ /**
178
+ * Allow the Regenerate Thumbnails plugin to copy the S3 file back to the local
179
+ * server when the file is missing on the server via get_attached_file
180
+ *
181
+ * @param string $url
182
+ * @param string $file
183
+ * @param int $attachment_id
184
+ * @param array $s3_object
185
+ *
186
+ * @return string
187
+ */
188
+ function regenerate_thumbnails_download_file( $url, $file, $attachment_id, $s3_object ) {
189
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
190
+ return $url;
191
+ }
192
+
193
+ if ( isset( $_POST['action'] ) && 'regeneratethumbnail' == sanitize_key( $_POST['action'] ) ) { // input var okay
194
+ if ( ( $file = $this->copy_s3_file_to_server( $s3_object, $file ) ) ) {
195
+ // Return the file if successfully downloaded from S3
196
+ return $file;
197
+ };
198
+ }
199
+
200
+ return $url;
201
+ }
202
+
203
+ /**
204
+ * Download a file from S3 if the file does not exist locally and places it where
205
+ * the attachment's file should be.
206
+ *
207
+ * @param array $s3_object
208
+ * @param string $file
209
+ *
210
+ * @return string|bool File if downloaded, false on failure
211
+ */
212
+ protected function copy_s3_file_to_server( $s3_object, $file ) {
213
+ try {
214
+ $this->as3cf->get_s3client( $s3_object['region'], true )->getObject(
215
+ array(
216
+ 'Bucket' => $s3_object['bucket'],
217
+ 'Key' => $s3_object['key'],
218
+ 'SaveAs' => $file,
219
+ )
220
+ );
221
+ } catch ( Exception $e ) {
222
+ error_log( sprintf( __( 'There was an error attempting to download the file %s from S3: %s', 'as3cf' ), $s3_object['key'], $e->getMessage() ) );
223
+
224
+ return false;
225
+ }
226
+
227
+ return $file;
228
+ }
229
+ }
classes/as3cf-upgrade.php CHANGED
@@ -77,24 +77,33 @@ class AS3CF_Upgrade {
77
  return;
78
  }
79
 
 
 
 
 
 
 
 
 
 
80
  // Initialize the upgrade
81
  $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
82
 
83
- $this->schedule_event();
84
  }
85
 
86
  /**
87
  * Adds notices about issues with upgrades allowing user to restart them
88
  */
89
  function maybe_display_notices() {
90
- $action_url = self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=restart_update_meta_with_region' );
91
- $msg_type = 'notice';
92
 
93
  switch ( $this->get_upgrade_status() ) {
94
  case self::STATUS_RUNNING :
95
  $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 );
96
  $action_text = __( 'Pause Update', 'as3cf' );
97
- $action_url = self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() . '&action=pause_update_meta_with_region' );
98
  break;
99
  case self::STATUS_PAUSED :
100
  $msg = __( '<strong>Metadata Update Paused</strong> &mdash; Updating Media Library metadata has been paused.', 'as3cf' );
@@ -111,7 +120,12 @@ class AS3CF_Upgrade {
111
 
112
  $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
113
 
114
- $this->as3cf->render_view( $msg_type, array( 'message' => $msg ) );
 
 
 
 
 
115
  }
116
 
117
  function maybe_handle_action() {
@@ -130,7 +144,7 @@ class AS3CF_Upgrade {
130
  */
131
  function action_restart_update_meta_with_region() {
132
  $this->change_status_request( self::STATUS_RUNNING );
133
- $this->schedule_event();
134
  }
135
 
136
  /**
@@ -151,7 +165,8 @@ class AS3CF_Upgrade {
151
  $session['status'] = $status;
152
  $this->save_session( $session );
153
 
154
- wp_redirect( self_admin_url( 'admin.php?page=' . $this->as3cf->get_plugin_slug() ) );
 
155
  }
156
 
157
  /**
@@ -170,63 +185,29 @@ class AS3CF_Upgrade {
170
 
171
  return $schedules;
172
  }
173
-
174
- /**
175
- * Wrapper for scheduling the cron job
176
- */
177
- function schedule_event() {
178
- if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
179
- wp_schedule_event( current_time( 'timestamp' ), self::CRON_SCHEDULE_KEY, self::CRON_HOOK );
180
- }
181
- }
182
-
183
- /**
184
- * Wrapper for clearing scheduled events for a specific cron job
185
- */
186
- function clear_scheduled_event() {
187
- $timestamp = wp_next_scheduled( self::CRON_HOOK );
188
- if ( $timestamp ) {
189
- wp_unschedule_event( $timestamp, self::CRON_HOOK );
190
- }
191
- }
192
-
193
  /**
194
  * Cron jon to update the region of the bucket in s3 metadata
195
  */
196
  function cron_update_meta_with_region() {
197
  // Check if the cron should even be running
198
  if ( $this->as3cf->get_setting( 'post_meta_version', 0 ) > 0 || $this->get_upgrade_status() != self::STATUS_RUNNING ) {
199
- $this->clear_scheduled_event();
200
  return;
201
  }
202
 
203
- global $wpdb;
204
- $prefix = $wpdb->prefix;
205
-
206
  // set the batch size limit for the query
207
  $limit = apply_filters( 'as3cf_update_meta_with_region_batch_size', 500 );
208
  $all_limit = $limit;
209
 
210
- $table_prefixes = array();
211
  $session = $this->get_session();
212
 
213
  // find the blog IDs that have been processed so we can skip them
214
  $processed_blog_ids = isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
215
  $error_count = isset( $session['error_count'] ) ? $session['error_count'] : 0;
216
 
217
- if ( ! in_array( 1, $processed_blog_ids ) ) {
218
- $table_prefixes[1] = $prefix;
219
- }
220
-
221
- if ( is_multisite() ) {
222
- $blog_ids = $this->as3cf->get_blog_ids();
223
- foreach ( $blog_ids as $blog_id ) {
224
- if ( in_array( $blog_id, $processed_blog_ids ) ) {
225
- continue;
226
- }
227
- $table_prefixes[ $blog_id ] = $prefix . $blog_id . '_';
228
- }
229
- }
230
 
231
  $all_attachments = array();
232
  $all_count = 0;
@@ -254,7 +235,7 @@ class AS3CF_Upgrade {
254
  $this->as3cf->set_setting( 'post_meta_version', 1 );
255
  $this->as3cf->remove_setting( 'update_meta_with_region_session' );
256
  $this->as3cf->save_settings();
257
- $this->clear_scheduled_event();
258
  return;
259
  }
260
 
@@ -268,7 +249,7 @@ class AS3CF_Upgrade {
268
 
269
  // loop through and update s3 meta with region
270
  foreach ( $all_attachments as $blog_id => $attachments ) {
271
- if ( 1 != $blog_id && is_multisite() ) {
272
  switch_to_blog( $blog_id );
273
  }
274
 
@@ -299,7 +280,7 @@ class AS3CF_Upgrade {
299
  }
300
  }
301
 
302
- if ( 1 != $blog_id && is_multisite() ) {
303
  restore_current_blog();
304
  }
305
  }
@@ -310,7 +291,26 @@ class AS3CF_Upgrade {
310
  $this->save_session( $session );
311
  }
312
 
313
- /*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  * Get the current status of the upgrade
315
  * See STATUS_* constants in the class declaration above.
316
  */
@@ -324,7 +324,7 @@ class AS3CF_Upgrade {
324
  return $session['status'];
325
  }
326
 
327
- /*
328
  * Retrieve session data from plugin settings
329
  *
330
  * @return array
@@ -333,7 +333,7 @@ class AS3CF_Upgrade {
333
  return $this->as3cf->get_setting( 'update_meta_with_region_session', array() );
334
  }
335
 
336
- /*
337
  * Store data to be used between requests in plugin settings
338
  *
339
  * @param $session array of session data to store
@@ -344,23 +344,90 @@ class AS3CF_Upgrade {
344
  }
345
 
346
  /**
347
- * Get all attachments that don't have region in their S3 meta
348
  *
349
- * @param unknown $prefix
350
- * @param unknown $limit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  *
352
  * @return mixed
353
  */
354
  function get_attachments_without_region( $prefix, $limit ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  global $wpdb;
356
- $sql = $wpdb->prepare(
357
- "SELECT `post_id` as `ID`, `meta_value` AS 's3object'
358
- FROM `{$prefix}postmeta`
359
  WHERE `meta_key` = 'amazonS3_info'
360
- AND `meta_value` NOT LIKE '%%\"region\"%%'
361
- LIMIT %d",
362
- $limit
363
- );
 
 
 
 
 
 
 
 
 
 
 
364
 
365
  return $wpdb->get_results( $sql, OBJECT );
366
  }
77
  return;
78
  }
79
 
80
+ // Do we actually have S3 meta data without regions to update?
81
+ // No need to bother for fresh sites, or media not uploaded to S3
82
+ if ( 0 == $this->count_all_attachments_without_region() ) {
83
+ $this->as3cf->set_setting( 'post_meta_version', 1 );
84
+ $this->as3cf->save_settings();
85
+
86
+ return;
87
+ }
88
+
89
  // Initialize the upgrade
90
  $this->save_session( array( 'status' => self::STATUS_RUNNING ) );
91
 
92
+ $this->as3cf->schedule_event( self::CRON_HOOK, self::CRON_SCHEDULE_KEY );
93
  }
94
 
95
  /**
96
  * Adds notices about issues with upgrades allowing user to restart them
97
  */
98
  function maybe_display_notices() {
99
+ $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'restart_update_meta_with_region' ), 'self' );
100
+ $msg_type = 'notice-info';
101
 
102
  switch ( $this->get_upgrade_status() ) {
103
  case self::STATUS_RUNNING :
104
  $msg = sprintf( __( '<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through all the Media Library items uploaded to S3 and updating the metadata with the bucket region it is served from. This will allow us to serve your files from the proper S3 region subdomain <span style="white-space:nowrap;">(e.g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the background, processing a small batch of Media Library items every %d minutes. There should be no noticeable impact on your server&#8217;s performance.', 'as3cf' ), $this->cron_interval_in_minutes );
105
  $action_text = __( 'Pause Update', 'as3cf' );
106
+ $action_url = $this->as3cf->get_plugin_page_url( array( 'action' => 'pause_update_meta_with_region' ), 'self' );
107
  break;
108
  case self::STATUS_PAUSED :
109
  $msg = __( '<strong>Metadata Update Paused</strong> &mdash; Updating Media Library metadata has been paused.', 'as3cf' );
120
 
121
  $msg .= ' <strong><a href="' . $action_url . '">' . $action_text . '</a></strong>';
122
 
123
+ $args = array(
124
+ 'message' => $msg,
125
+ 'type' => $msg_type,
126
+ );
127
+
128
+ $this->as3cf->render_view( 'notice', $args );
129
  }
130
 
131
  function maybe_handle_action() {
144
  */
145
  function action_restart_update_meta_with_region() {
146
  $this->change_status_request( self::STATUS_RUNNING );
147
+ $this->as3cf->schedule_event( self::CRON_HOOK, self::CRON_SCHEDULE_KEY );
148
  }
149
 
150
  /**
165
  $session['status'] = $status;
166
  $this->save_session( $session );
167
 
168
+ $url = $this->as3cf->get_plugin_page_url( array(), 'self' );
169
+ wp_redirect( $url );
170
  }
171
 
172
  /**
185
 
186
  return $schedules;
187
  }
188
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  /**
190
  * Cron jon to update the region of the bucket in s3 metadata
191
  */
192
  function cron_update_meta_with_region() {
193
  // Check if the cron should even be running
194
  if ( $this->as3cf->get_setting( 'post_meta_version', 0 ) > 0 || $this->get_upgrade_status() != self::STATUS_RUNNING ) {
195
+ $this->as3cf->clear_scheduled_event( self::CRON_HOOK );
196
  return;
197
  }
198
 
 
 
 
199
  // set the batch size limit for the query
200
  $limit = apply_filters( 'as3cf_update_meta_with_region_batch_size', 500 );
201
  $all_limit = $limit;
202
 
 
203
  $session = $this->get_session();
204
 
205
  // find the blog IDs that have been processed so we can skip them
206
  $processed_blog_ids = isset( $session['processed_blog_ids'] ) ? $session['processed_blog_ids'] : array();
207
  $error_count = isset( $session['error_count'] ) ? $session['error_count'] : 0;
208
 
209
+ // get the table prefixes for all the blogs
210
+ $table_prefixes = $this->get_all_blog_table_prefixes( $processed_blog_ids );
 
 
 
 
 
 
 
 
 
 
 
211
 
212
  $all_attachments = array();
213
  $all_count = 0;
235
  $this->as3cf->set_setting( 'post_meta_version', 1 );
236
  $this->as3cf->remove_setting( 'update_meta_with_region_session' );
237
  $this->as3cf->save_settings();
238
+ $this->as3cf->clear_scheduled_event( self::CRON_HOOK );
239
  return;
240
  }
241
 
249
 
250
  // loop through and update s3 meta with region
251
  foreach ( $all_attachments as $blog_id => $attachments ) {
252
+ if ( is_multisite() && ! $this->as3cf->is_current_blog( $blog_id ) ) {
253
  switch_to_blog( $blog_id );
254
  }
255
 
280
  }
281
  }
282
 
283
+ if ( is_multisite() && ! $this->as3cf->is_current_blog( $blog_id ) ) {
284
  restore_current_blog();
285
  }
286
  }
291
  $this->save_session( $session );
292
  }
293
 
294
+ /**
295
+ * Get a count of all attachments without region in their S3 metadata
296
+ * for the whole site
297
+ *
298
+ * @return int
299
+ */
300
+ function count_all_attachments_without_region() {
301
+ // get the table prefixes for all the blogs
302
+ $table_prefixes = $this->get_all_blog_table_prefixes();
303
+ $all_count = 0;
304
+
305
+ foreach ( $table_prefixes as $blog_id => $table_prefix ) {
306
+ $count = $this->count_attachments_without_region( $table_prefix );
307
+ $all_count += $count;
308
+ }
309
+
310
+ return $all_count;
311
+ }
312
+
313
+ /**
314
  * Get the current status of the upgrade
315
  * See STATUS_* constants in the class declaration above.
316
  */
324
  return $session['status'];
325
  }
326
 
327
+ /**
328
  * Retrieve session data from plugin settings
329
  *
330
  * @return array
333
  return $this->as3cf->get_setting( 'update_meta_with_region_session', array() );
334
  }
335
 
336
+ /**
337
  * Store data to be used between requests in plugin settings
338
  *
339
  * @param $session array of session data to store
344
  }
345
 
346
  /**
347
+ * Get all the table prefixes for the blogs in the site. MS compatible
348
  *
349
+ * @param array $exclude_blog_ids blog ids to exclude
350
+ *
351
+ * @return array associative array with blog ID as key, prefix as value
352
+ */
353
+ function get_all_blog_table_prefixes( $exclude_blog_ids = array() ) {
354
+ global $wpdb;
355
+ $prefix = $wpdb->prefix;
356
+
357
+ $table_prefixes = array();
358
+
359
+ if ( ! in_array( 1, $exclude_blog_ids ) ) {
360
+ $table_prefixes[1] = $prefix;
361
+ }
362
+
363
+ if ( is_multisite() ) {
364
+ $blog_ids = $this->as3cf->get_blog_ids();
365
+ foreach ( $blog_ids as $blog_id ) {
366
+ if ( in_array( $blog_id, $exclude_blog_ids ) ) {
367
+ continue;
368
+ }
369
+ $table_prefixes[ $blog_id ] = $wpdb->get_blog_prefix( $blog_id );
370
+ }
371
+ }
372
+
373
+ return $table_prefixes;
374
+ }
375
+
376
+ /**
377
+ * Get all attachments that don't have region in their S3 meta data for a blog
378
+ *
379
+ * @param string $prefix
380
+ * @param int $limit
381
  *
382
  * @return mixed
383
  */
384
  function get_attachments_without_region( $prefix, $limit ) {
385
+ $attachments = $this->get_attachments_without_region_results( $prefix, false, $limit );
386
+
387
+ return $attachments;
388
+ }
389
+
390
+ /**
391
+ * Get a count of attachments that don't have region in their S3 meta data for a blog
392
+ * @param $prefix
393
+ *
394
+ * @return int
395
+ */
396
+ function count_attachments_without_region( $prefix ) {
397
+ $count = $this->get_attachments_without_region_results( $prefix, true );
398
+
399
+ return $count;
400
+ }
401
+
402
+ /**
403
+ * Wrapper for database call to get attachments without region
404
+ *
405
+ * @param string $prefix
406
+ * @param bool $count return count of attachments
407
+ * @param null|int $limit
408
+ *
409
+ * @return mixed
410
+ */
411
+ function get_attachments_without_region_results( $prefix, $count = false, $limit = null ) {
412
  global $wpdb;
413
+
414
+ $sql = " FROM `{$prefix}postmeta`
 
415
  WHERE `meta_key` = 'amazonS3_info'
416
+ AND `meta_value` NOT LIKE '%%\"region\"%%'";
417
+
418
+ if ( $count ) {
419
+ $sql = 'SELECT COUNT(*)' . $sql;
420
+
421
+ return $wpdb->get_var( $sql );
422
+ }
423
+
424
+ $sql = "SELECT `post_id` as `ID`, `meta_value` AS 's3object'" . $sql;
425
+
426
+ if ( ! is_null( $limit ) ) {
427
+ $sql .= ' LIMIT %d';
428
+
429
+ $sql = $wpdb->prepare( $sql, $limit );
430
+ }
431
 
432
  return $wpdb->get_results( $sql, OBJECT );
433
  }
classes/wp-aws-compatibility-check.php ADDED
@@ -0,0 +1,424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WP AWS Compatibility Check
4
+ *
5
+ * @package wp-aws
6
+ * @copyright Copyright (c) 2015, Delicious Brains
7
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
+ * @since 0.1
9
+ */
10
+
11
+ // Exit if accessed directly
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ // Check if already defined
17
+ if ( ! class_exists( 'WP_AWS_Compatibility_Check' ) ) {
18
+
19
+ /**
20
+ * WP_AWS_Compatibility_Check Class
21
+ *
22
+ * This class handles compatibility between an AWS plugin and a required parent plugin
23
+ *
24
+ * @since 0.1
25
+ */
26
+ class WP_AWS_Compatibility_Check {
27
+ /**
28
+ * @var string The derived key of the plugin from the name, e.g. amazon-s3-and-cloudfront
29
+ */
30
+ protected $plugin_slug;
31
+
32
+ /**
33
+ * @var string The name of the plugin, e.g. WP Offload S3 - Pro Upgrade
34
+ */
35
+ protected $plugin_name;
36
+
37
+ /**
38
+ * @var string The file path to the plugin's main file
39
+ */
40
+ protected $plugin_file_path;
41
+
42
+ /**
43
+ * @var null|string The key of the required parent plugin, e.g. amazon-web-services
44
+ */
45
+ protected $parent_plugin_slug;
46
+
47
+ /**
48
+ * @var null|int The required version of the parent plugin
49
+ */
50
+ protected $parent_plugin_required_version;
51
+
52
+ /**
53
+ * @var null|string Optional name of the required parent plugin's filename if different to {$parent_plugin}.php
54
+ */
55
+ protected $parent_plugin_filename;
56
+
57
+ /**
58
+ * @var bool Do we deactivate the plugin if the requirements are not met?
59
+ */
60
+ protected $deactivate_if_not_compatible;
61
+
62
+ /**
63
+ * @var string The URL of the plugin if not on the WordPress.org repo
64
+ */
65
+ protected $parent_plugin_url;
66
+
67
+ /**
68
+ * @var string The error message to display in the admin notice
69
+ */
70
+ protected $error_message;
71
+
72
+ /**
73
+ * @var string The CSS class for the notice
74
+ */
75
+ protected $notice_class = 'error';
76
+
77
+ function __construct( $plugin_name, $plugin_slug, $plugin_file_path, $parent_plugin_name = null, $parent_plugin_slug = null, $parent_plugin_required_version = null, $parent_plugin_filename = null, $deactivate_if_not_compatible = false, $parent_plugin_url = null ) {
78
+ $this->plugin_name = $plugin_name;
79
+ $this->plugin_slug = $plugin_slug;
80
+ $this->plugin_file_path = $plugin_file_path;
81
+ $this->parent_plugin_name = $parent_plugin_name;
82
+ $this->parent_plugin_slug = $parent_plugin_slug;
83
+ $this->parent_plugin_required_version = $parent_plugin_required_version;
84
+ $this->parent_plugin_filename = $parent_plugin_filename;
85
+ $this->deactivate_if_not_compatible = $deactivate_if_not_compatible;
86
+ $this->parent_plugin_url = $parent_plugin_url;
87
+
88
+ add_action( 'admin_notices', array( $this, 'hook_admin_notices' ) );
89
+ add_action( 'network_admin_notices', array( $this, 'hook_admin_notices' ) );
90
+ }
91
+
92
+ /**
93
+ * Is the plugin compatible?
94
+ *
95
+ * @return bool
96
+ */
97
+ function is_compatible() {
98
+ $compatible = $this->get_error_msg() ? false : true;
99
+
100
+ $GLOBALS['aws_meta'][ $this->plugin_slug ]['compatible'] = $compatible;
101
+
102
+ return $compatible;
103
+ }
104
+
105
+ /**
106
+ * Get the basename for the plugin
107
+ *
108
+ * @return string
109
+ */
110
+ function get_plugin_basename() {
111
+ return plugin_basename( $this->plugin_file_path );
112
+ }
113
+
114
+ /**
115
+ * Get the name of the parent plugin
116
+ *
117
+ * @return string
118
+ */
119
+ function get_parent_plugin_name() {
120
+ if ( $this->parent_plugin_name ) {
121
+ return $this->parent_plugin_name;
122
+ }
123
+
124
+ return '';
125
+ }
126
+
127
+ /**
128
+ * Get the class of the parent plugin
129
+ *
130
+ * @return mixed|string
131
+ */
132
+ function get_parent_plugin_class() {
133
+ if ( $this->parent_plugin_slug ) {
134
+ $class = ucwords( str_replace( '-', ' ', $this->parent_plugin_slug ) );
135
+
136
+ return str_replace( ' ', '_', $class );
137
+ }
138
+
139
+ return '';
140
+ }
141
+
142
+ /**
143
+ * Get the filename of the main parent plugin file
144
+ *
145
+ * @return string
146
+ */
147
+ function get_parent_plugin_filename() {
148
+ if ( ! is_null( $this->parent_plugin_slug ) ) {
149
+ $filename = $this->parent_plugin_slug;
150
+ if ( ! is_null( $this->parent_plugin_filename ) ) {
151
+ $filename = basename( $this->parent_plugin_filename, '.php' );
152
+ }
153
+
154
+ return $filename . '.php';
155
+ }
156
+
157
+ return '';
158
+ }
159
+
160
+ /**
161
+ * Get the basename of the parent plugin {slug}/{slug}.php
162
+ *
163
+ * @return string
164
+ */
165
+ function get_parent_plugin_basename() {
166
+ if ( ! is_null( $this->parent_plugin_slug ) ) {
167
+ $file_name = $this->get_parent_plugin_filename();
168
+
169
+ return $this->parent_plugin_slug . '/' . $file_name;
170
+ }
171
+
172
+ return '';
173
+ }
174
+
175
+ /**
176
+ * Get the URL for the parent plugin. Defaults to a wordpress.org URL.
177
+ *
178
+ * @return null|string
179
+ */
180
+ function get_parent_plugin_url() {
181
+ if ( ! is_null( $this->parent_plugin_slug ) ) {
182
+ $url = 'http://wordpress.org/extend/plugins/' . $this->parent_plugin_slug . '/';
183
+ if ( ! is_null( $this->parent_plugin_url ) ) {
184
+ $url = $this->parent_plugin_url;
185
+ }
186
+
187
+ return $url;
188
+ }
189
+
190
+ return '';
191
+ }
192
+
193
+ /**
194
+ * Generate a URL to perform core actions on for a plugin
195
+ *
196
+ * @param string $action Such as activate, deactivate, install, upgrade
197
+ * @param string|null $basename
198
+ *
199
+ * @return string
200
+ */
201
+ function get_plugin_action_url( $action, $basename = null ) {
202
+ if ( is_null( $basename ) ) {
203
+ $basename = $this->get_plugin_basename();
204
+ }
205
+
206
+ $nonce_action = $action . '-plugin_' . $basename;
207
+ $page = 'plugins';
208
+
209
+ if ( in_array( $action, array( 'upgrade', 'install' ) ) ) {
210
+ $page = 'update';
211
+ $action .= '-plugin';
212
+ }
213
+
214
+ $url = wp_nonce_url( network_admin_url( $page . '.php?action=' . $action . '&amp;plugin=' . $basename ), $nonce_action );
215
+
216
+ return $url;
217
+ }
218
+
219
+ /**
220
+ * Set the error message to be returned for the admin notice
221
+ *
222
+ * @param string $message
223
+ *
224
+ * @return string
225
+ */
226
+ function set_error_msg( $message ) {
227
+ // Replace the space between the last two words with &nbsp; to prevent typographic widows
228
+ $message = preg_replace( '/\s([\w]+[.,!\:;\\"-?]{0,1})$/', '&nbsp;\\1', $message, 1 );
229
+
230
+ $this->error_message = $message;
231
+
232
+ return $this->error_message;
233
+ }
234
+
235
+ /**
236
+ * Check if the parent plugin is active and enabled, ie. not disabled due to
237
+ * compatibility issues up the chain.
238
+ *
239
+ * @return bool
240
+ */
241
+ function is_parent_plugin_enabled() {
242
+ $class = $this->get_parent_plugin_class();
243
+ if ( ! class_exists( $class ) ) {
244
+ // Class not even loaded
245
+ return false;
246
+ }
247
+
248
+ // call_user_func overcomes parse errors on PHP versions < 5.3
249
+ if ( method_exists( $class, 'is_compatible' ) && ! call_user_func( array( $class, 'is_compatible' ) ) ) {
250
+ // The plugin is active but not compatible
251
+ return false;
252
+ }
253
+
254
+ return true;
255
+ }
256
+
257
+ /**
258
+ * Get the compatibility error message
259
+ *
260
+ * @return string|void
261
+ */
262
+ function get_error_msg() {
263
+ if ( is_null( $this->parent_plugin_slug ) ) {
264
+ return false;
265
+ }
266
+
267
+ if ( ! is_null( $this->error_message ) ) {
268
+ return $this->error_message;
269
+ }
270
+
271
+ $plugin_basename = $this->get_plugin_basename();
272
+ $parent_basename = $this->get_parent_plugin_basename();
273
+ $parent_plugin_link_html = sprintf( '<a style="text-decoration:none;" href="%s">%s</a>', $this->get_parent_plugin_url(), $this->get_parent_plugin_name() );
274
+
275
+ $deactivate_url = $this->get_plugin_action_url( 'deactivate', $plugin_basename );
276
+ $deactivate_link = sprintf( '<a style="text-decoration:none;" href="%s">%s</a>', $deactivate_url, __( 'deactivate', 'as3cf' ) );
277
+ $hide_notice_msg = '<br><em>' . sprintf( __( 'You can %s the %s plugin to get rid of this notice.', 'as3cf' ), $deactivate_link, $this->plugin_name ) . '</em>';
278
+
279
+ if ( ! $this->is_parent_plugin_enabled() ) {
280
+ $msg = sprintf( __( '%s has been disabled as it requires the %s plugin.', 'as3cf' ), $this->plugin_name, $parent_plugin_link_html );
281
+
282
+ if ( file_exists( WP_PLUGIN_DIR . '/' . $parent_basename ) ) {
283
+ if ( isset( $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['compatible'] ) && ! $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['compatible'] ) {
284
+ $msg = rtrim( $msg, '.' ) . ', ' . __( 'which is currently disabled.', 'as3cf' );
285
+ } else {
286
+ $msg .= ' ' . __( 'It appears to be installed already.', 'as3cf' );
287
+ $activate_url = $this->get_plugin_action_url( 'activate', $parent_basename );
288
+ $msg .= ' <a style="font-weight:bold;text-decoration:none;" href="' . $activate_url . '">' . _x( 'Activate it now.', 'Activate plugin', 'as3cf' ) . '</a>';
289
+ }
290
+ } else {
291
+ $install_url = 'https://deliciousbrains.com/my-account/';
292
+ if ( is_null( $this->parent_plugin_url ) ) {
293
+ $install_url = $this->get_plugin_action_url( 'install', $this->parent_plugin_slug );
294
+ }
295
+ $msg .= ' ' . sprintf( __( '<a href="%s">Install</a> and activate it.', 'as3cf' ), $install_url );
296
+ }
297
+
298
+ $msg .= $hide_notice_msg;
299
+
300
+ return $this->set_error_msg( $msg );
301
+ }
302
+
303
+ $current_parent_plugin_version = isset( $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['version'] ) ? $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['version'] : 0;
304
+
305
+ if ( ! version_compare( $current_parent_plugin_version, $this->parent_plugin_required_version, '>=' ) ) {
306
+ $msg = sprintf( __( '%s has been disabled as it requires version %s or later of the %s plugin.', 'as3cf' ), $this->plugin_name, $this->parent_plugin_required_version, $parent_plugin_link_html );
307
+
308
+ if ( $current_parent_plugin_version ) {
309
+ $msg .= ' ' . sprintf( __( 'You currently have version %s installed.', 'as3cf' ), $current_parent_plugin_version );
310
+ }
311
+
312
+ global $as3cfpro;
313
+ if ( ! empty( $as3cfpro ) && $as3cfpro->get_plugin_slug() === $this->parent_plugin_slug ) {
314
+ // Don't show update link for addons of a licensed plugin where the license is invalid
315
+ if ( ! $as3cfpro->is_valid_licence() ) {
316
+ $msg .= ' ' . sprintf( __( 'A valid license for %s is required to update.', 'as3cf' ), $this->get_parent_plugin_name() );
317
+ $msg .= $hide_notice_msg;
318
+
319
+ return $this->set_error_msg( $msg );
320
+ }
321
+ }
322
+
323
+ $update_url = $this->get_plugin_action_url( 'upgrade', $parent_basename );
324
+ $msg .= ' <a style="font-weight:bold;text-decoration:none;white-space:nowrap;" href="' . $update_url . '">' . __( 'Update to the latest version', 'as3cf' ) . '</a>';
325
+
326
+ $msg .= $hide_notice_msg;
327
+
328
+ return $this->set_error_msg( $msg );
329
+ }
330
+
331
+ if ( ! isset( $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['supported_addon_versions'] ) ) {
332
+ return false;
333
+ }
334
+
335
+ if ( ! isset( $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['supported_addon_versions'][ $this->plugin_slug ] ) ) {
336
+ $msg = sprintf( __( '%1$s has been disabled because it is not a supported addon of the %2$s plugin.', 'as3cf' ), $this->plugin_name, $this->get_parent_plugin_name() );
337
+
338
+ return $this->set_error_msg( $msg );
339
+ }
340
+
341
+ $this_plugin_version_required = $GLOBALS['aws_meta'][ $this->parent_plugin_slug ]['supported_addon_versions'][ $this->plugin_slug ];
342
+ $this_plugin_version = $GLOBALS['aws_meta'][ $this->plugin_slug ]['version'];
343
+
344
+ if ( ! version_compare( $this_plugin_version, $this_plugin_version_required, '>=' ) ) {
345
+ $msg = sprintf( __( '%1$s has been disabled because it will not work with the version of the %2$s plugin installed. %1$s %3$s or later is required.', 'as3cf' ), $this->plugin_name, $this->get_parent_plugin_name(), $this_plugin_version_required );
346
+
347
+ $update_url = $this->get_plugin_action_url( 'upgrade', $plugin_basename );
348
+ $msg .= ' <a style="font-weight:bold;text-decoration:none;white-space:nowrap;" href="' . $update_url . '">' . sprintf( __( 'Update %s to the latest version', 'as3cf' ), $this->plugin_name ) . '</a>';
349
+
350
+ $msg .= $hide_notice_msg;
351
+
352
+ return $this->set_error_msg( $msg );
353
+ }
354
+
355
+ return false;
356
+ }
357
+
358
+ /**
359
+ * Check plugin capabilities for a user
360
+ *
361
+ * @return bool
362
+ */
363
+ function check_capabilities() {
364
+ if ( is_multisite() ) {
365
+ if ( ! current_user_can( 'manage_network_plugins' ) ) {
366
+ return false; // Don't allow if the user can't manage network plugins
367
+ }
368
+ } else {
369
+ // Don't allow if user doesn't have plugin management privileges
370
+ $caps = array( 'activate_plugins', 'update_plugins', 'install_plugins' );
371
+ foreach ( $caps as $cap ) {
372
+ if ( ! current_user_can( $cap ) ) {
373
+ return false;
374
+ }
375
+ }
376
+ }
377
+
378
+ return true;
379
+ }
380
+
381
+ /**
382
+ * Display compatibility notices to users who can manage plugins
383
+ */
384
+ function hook_admin_notices() {
385
+ if ( ! $this->check_capabilities() ){
386
+ return;
387
+ }
388
+
389
+ $this->get_admin_notice();
390
+ }
391
+
392
+ /**
393
+ * Get the admin notice to be displayed
394
+ */
395
+ function get_admin_notice() {
396
+ $error_msg = $this->get_error_msg();
397
+
398
+ if ( ! $error_msg ) {
399
+ return;
400
+ }
401
+
402
+ if ( $this->deactivate_if_not_compatible ) {
403
+ $deactivated_msg = sprintf( __( 'The %s plugin has been deactivated.', 'as3cf' ), $this->plugin_name );
404
+
405
+ $error_msg = $deactivated_msg . ' ' . $error_msg;
406
+ $this->render_notice( $error_msg );
407
+
408
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
409
+ deactivate_plugins( $this->plugin_file_path );
410
+ } else {
411
+ $this->render_notice( $error_msg );
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Render the notice HTML
417
+ *
418
+ * @param string $message
419
+ */
420
+ function render_notice( $message ) {
421
+ printf( '<div class="' . $this->notice_class . ' aws-compatibility-notice"><p>%s</p></div>', $message );
422
+ }
423
+ }
424
+ }
classes/wp-aws-uninstall.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WP AWS Uninstall
4
+ *
5
+ * @package wp-aws
6
+ * @copyright Copyright (c) 2015, Delicious Brains
7
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
+ * @since 0.1
9
+ */
10
+
11
+ // Exit if accessed directly
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ // Check if already defined
17
+ if ( ! class_exists( 'WP_AWS_Uninstall' ) ) {
18
+
19
+ /**
20
+ * WP_AWS_Uninstall Class
21
+ *
22
+ * This class handles shared functions for uninstalling AWS plugins
23
+ *
24
+ * @since 0.1
25
+ */
26
+ class WP_AWS_Uninstall {
27
+
28
+ /**
29
+ * @var array Options to be deleted
30
+ */
31
+ protected $options;
32
+
33
+ /**
34
+ * @var array Post meta to be deleted
35
+ */
36
+ protected $postmeta;
37
+
38
+ /**
39
+ * @var array Cron hooks to be unscheduled
40
+ */
41
+ protected $crons;
42
+
43
+ /**
44
+ * @var array Transients to be deleted
45
+ */
46
+ protected $transients;
47
+
48
+ /**
49
+ * @var Blog(s) in site
50
+ */
51
+ protected $blog_ids;
52
+
53
+ /**
54
+ * WP_AWS_Uninstall constructor.
55
+ *
56
+ * @param array $options
57
+ * @param array $postmeta
58
+ * @param array $crons
59
+ * @param array $transients
60
+ */
61
+ public function __construct(
62
+ $options = array(),
63
+ $postmeta = array(),
64
+ $crons = array(),
65
+ $transients = array()
66
+ ) {
67
+ $this->options = $options;
68
+ $this->postmeta = $postmeta;
69
+ $this->crons = $crons;
70
+ $this->transients = $transients;
71
+
72
+ $this->set_blog_ids();
73
+
74
+ $this->delete_options();
75
+ $this->delete_postmeta();
76
+ $this->clear_crons();
77
+ $this->delete_transients();
78
+ }
79
+
80
+ /**
81
+ * Set the blog id(s) for a site
82
+ */
83
+ private function set_blog_ids() {
84
+ $blog_ids[] = 1;
85
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
86
+ $args = array(
87
+ 'limit' => false,
88
+ 'spam' => 0,
89
+ 'deleted' => 0,
90
+ 'archived' => 0,
91
+ );
92
+ $blogs = wp_get_sites( $args );
93
+ $blog_ids = wp_list_pluck( $blogs, 'blog_id' );
94
+ }
95
+
96
+ $this->blog_ids = $blog_ids;
97
+ }
98
+
99
+ /**
100
+ * Check and ensure a property has been filled with an array
101
+ *
102
+ * @param string $property
103
+ *
104
+ * @return bool
105
+ */
106
+ private function check_property( $property ) {
107
+ if ( empty( $this->$property ) ) {
108
+ return false;
109
+ }
110
+
111
+ if ( ! is_array( $this->$property ) ) {
112
+ // Convert any strings to an array
113
+ $this->$property = array( $this->$property );
114
+ }
115
+
116
+ return true;
117
+ }
118
+
119
+ /**
120
+ * Delete site wide options
121
+ */
122
+ public function delete_options() {
123
+ if ( ! $this->check_property( 'options' ) ) {
124
+ return;
125
+ }
126
+
127
+ foreach ( $this->options as $option ) {
128
+ delete_site_option( $option );
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Delete post meta data for all blogs
134
+ */
135
+ public function delete_postmeta() {
136
+ if ( ! $this->check_property( 'postmeta' ) ) {
137
+ return;
138
+ }
139
+
140
+ global $wpdb;
141
+
142
+ foreach ( $this->blog_ids as $blog_id ) {
143
+ $prefix = $wpdb->get_blog_prefix( $blog_id );
144
+
145
+ foreach ( $this->postmeta as $postmeta ) {
146
+ $sql = $wpdb->prepare( "DELETE FROM {$prefix}postmeta WHERE meta_key = %s", $postmeta );
147
+ $wpdb->query( $sql );
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Clear any scheduled cron jobs
154
+ */
155
+ public function clear_crons() {
156
+ if ( ! $this->check_property( 'crons' ) ) {
157
+ return;
158
+ }
159
+
160
+ foreach ( $this->crons as $cron ) {
161
+ $timestamp = wp_next_scheduled( $cron );
162
+ if ( $timestamp ) {
163
+ wp_unschedule_event( $timestamp, $cron );
164
+ }
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Delete site wide transients
170
+ */
171
+ public function delete_transients() {
172
+ if ( ! $this->check_property( 'transients' ) ) {
173
+ return;
174
+ }
175
+
176
+ foreach ( $this->transients as $transient ) {
177
+ delete_site_transient( $transient );
178
+ }
179
+ }
180
+ }
181
+ }
include/functions.php CHANGED
@@ -11,4 +11,4 @@ function as3cf_get_secure_attachment_url( $post_id, $expires = 900, $size = null
11
  global $as3cf;
12
 
13
  return $as3cf->get_secure_attachment_url( $post_id, $expires, $size );
14
- }
11
  global $as3cf;
12
 
13
  return $as3cf->get_secure_attachment_url( $post_id, $expires, $size );
14
+ }
languages/amazon-s3-and-cloudfront-en.pot ADDED
@@ -0,0 +1,580 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: amazon-s3-and-cloudfront\n"
10
+ "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n"
11
+ "POT-Creation-Date: 2015-07-08 12:57-0300\n"
12
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=CHARSET\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+
20
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:79
21
+ msgid "Offload S3"
22
+ msgstr ""
23
+
24
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:80
25
+ msgid "S3 and CloudFront"
26
+ msgstr ""
27
+
28
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:568
29
+ #, php-format
30
+ msgid "File %s does not exist"
31
+ msgstr ""
32
+
33
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:577
34
+ #, php-format
35
+ msgid "Mime type %s is not allowed"
36
+ msgstr ""
37
+
38
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:653
39
+ #, php-format
40
+ msgid "Error uploading %s to S3: %s"
41
+ msgstr ""
42
+
43
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1251
44
+ msgid "Cheatin&#8217; eh?"
45
+ msgstr ""
46
+
47
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1255
48
+ msgid "You do not have sufficient permissions to access this page."
49
+ msgstr ""
50
+
51
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1261
52
+ msgid "No bucket name provided."
53
+ msgstr ""
54
+
55
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1518
56
+ #, php-format
57
+ msgid "There was an error attempting to get the region of the bucket %s: %s"
58
+ msgstr ""
59
+
60
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1635
61
+ msgid ""
62
+ "This is a test file to check if the user has write permission to S3. Delete "
63
+ "me if found."
64
+ msgstr ""
65
+
66
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1667
67
+ #, php-format
68
+ msgid ""
69
+ "There was an error attempting to check the permissions of the bucket %s: %s"
70
+ msgstr ""
71
+
72
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1730
73
+ msgid "Error creating bucket: "
74
+ msgstr ""
75
+
76
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1731
77
+ msgid "Bucket name too short."
78
+ msgstr ""
79
+
80
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1732
81
+ msgid "Bucket name too long."
82
+ msgstr ""
83
+
84
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1733
85
+ msgid ""
86
+ "Invalid character. Bucket names can contain lowercase letters, numbers, "
87
+ "periods and hyphens."
88
+ msgstr ""
89
+
90
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1734
91
+ msgid "Error saving bucket: "
92
+ msgstr ""
93
+
94
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1735
95
+ msgid "Error fetching buckets: "
96
+ msgstr ""
97
+
98
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1736
99
+ msgid "Error getting URL preview: "
100
+ msgstr ""
101
+
102
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1737
103
+ msgid "The changes you made will be lost if you navigate away from this page"
104
+ msgstr ""
105
+
106
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1795
107
+ msgid "Cheatin' eh?"
108
+ msgstr ""
109
+
110
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1898
111
+ msgctxt "Show the media library tab"
112
+ msgid "Media Library"
113
+ msgstr ""
114
+
115
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:1899
116
+ msgctxt "Show the support tab"
117
+ msgid "Support"
118
+ msgstr ""
119
+
120
+ #: builds/amazon-s3-and-cloudfront/classes/amazon-s3-and-cloudfront.php:2035
121
+ #, php-format
122
+ msgid "The file %s has been given %s permissions on Amazon S3."
123
+ msgstr ""
124
+
125
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-plugin-compatibility.php:222
126
+ #, php-format
127
+ msgid "There was an error attempting to download the file %s from S3: %s"
128
+ msgstr ""
129
+
130
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:104
131
+ #, php-format
132
+ msgid ""
133
+ "<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through "
134
+ "all the Media Library items uploaded to S3 and updating the metadata with "
135
+ "the bucket region it is served from. This will allow us to serve your files "
136
+ "from the proper S3 region subdomain <span style=\"white-space:nowrap;\">(e."
137
+ "g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the "
138
+ "background, processing a small batch of Media Library items every %d "
139
+ "minutes. There should be no noticeable impact on your server&#8217;s "
140
+ "performance."
141
+ msgstr ""
142
+
143
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:105
144
+ msgid "Pause Update"
145
+ msgstr ""
146
+
147
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:109
148
+ msgid ""
149
+ "<strong>Metadata Update Paused</strong> &mdash; Updating Media Library "
150
+ "metadata has been paused."
151
+ msgstr ""
152
+
153
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:110
154
+ msgid "Restart Update"
155
+ msgstr ""
156
+
157
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:113
158
+ msgid ""
159
+ "<strong>Error Updating Metadata</strong> &mdash; We ran into some errors "
160
+ "attempting to update the metadata for all your Media Library items that have "
161
+ "been uploaded to S3. Please check your error log for details."
162
+ msgstr ""
163
+
164
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:114
165
+ msgid "Try Run It Again"
166
+ msgstr ""
167
+
168
+ #: builds/amazon-s3-and-cloudfront/classes/as3cf-upgrade.php:183
169
+ #, php-format
170
+ msgid "Every %d Minutes"
171
+ msgstr ""
172
+
173
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:276
174
+ msgid "deactivate"
175
+ msgstr ""
176
+
177
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:277
178
+ #, php-format
179
+ msgid "You can %s the %s plugin to get rid of this notice."
180
+ msgstr ""
181
+
182
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:280
183
+ #, php-format
184
+ msgid "%s has been disabled as it requires the %s plugin."
185
+ msgstr ""
186
+
187
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:284
188
+ msgid "which is currently disabled."
189
+ msgstr ""
190
+
191
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:286
192
+ msgid "It appears to be installed already."
193
+ msgstr ""
194
+
195
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:288
196
+ msgctxt "Activate plugin"
197
+ msgid "Activate it now."
198
+ msgstr ""
199
+
200
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:295
201
+ #, php-format
202
+ msgid "<a href=\"%s\">Install</a> and activate it."
203
+ msgstr ""
204
+
205
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:306
206
+ #, php-format
207
+ msgid ""
208
+ "%s has been disabled as it requires version %s or later of the %s plugin."
209
+ msgstr ""
210
+
211
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:309
212
+ #, php-format
213
+ msgid "You currently have version %s installed."
214
+ msgstr ""
215
+
216
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:316
217
+ #, php-format
218
+ msgid "A valid license for %s is required to update."
219
+ msgstr ""
220
+
221
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:324
222
+ msgid "Update to the latest version"
223
+ msgstr ""
224
+
225
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:336
226
+ #, php-format
227
+ msgid ""
228
+ "%1$s has been disabled because it is not a supported addon of the %2$s "
229
+ "plugin."
230
+ msgstr ""
231
+
232
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:345
233
+ #, php-format
234
+ msgid ""
235
+ "%1$s has been disabled because it will not work with the version of the %2$s "
236
+ "plugin installed. %1$s %3$s or later is required."
237
+ msgstr ""
238
+
239
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:348
240
+ #, php-format
241
+ msgid "Update %s to the latest version"
242
+ msgstr ""
243
+
244
+ #: builds/amazon-s3-and-cloudfront/classes/wp-aws-compatibility-check.php:403
245
+ #, php-format
246
+ msgid "The %s plugin has been deactivated."
247
+ msgstr ""
248
+
249
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:3
250
+ msgid "Change bucket"
251
+ msgstr ""
252
+
253
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:3
254
+ msgid "What bucket would you like to use?"
255
+ msgstr ""
256
+
257
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:5
258
+ msgid "Existing bucket name"
259
+ msgstr ""
260
+
261
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:7
262
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:12
263
+ msgid "Saving..."
264
+ msgstr ""
265
+
266
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:7
267
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:12
268
+ msgid "Save Bucket"
269
+ msgstr ""
270
+
271
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:8
272
+ msgid "Browse existing buckets"
273
+ msgstr ""
274
+
275
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:9
276
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:26
277
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:31
278
+ msgid "Create new bucket"
279
+ msgstr ""
280
+
281
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:13
282
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:21
283
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:61
284
+ msgid "Cancel"
285
+ msgstr ""
286
+
287
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:18
288
+ msgid "Select bucket"
289
+ msgstr ""
290
+
291
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:19
292
+ msgid "Loading..."
293
+ msgstr ""
294
+
295
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:22
296
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:27
297
+ msgid "Refresh"
298
+ msgstr ""
299
+
300
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:25
301
+ msgid "Enter bucket name"
302
+ msgstr ""
303
+
304
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:37
305
+ msgid "Bucket Name:"
306
+ msgstr ""
307
+
308
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:40
309
+ msgid "Bucket Name"
310
+ msgstr ""
311
+
312
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:47
313
+ msgid "Region:"
314
+ msgstr ""
315
+
316
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:60
317
+ msgid "Creating..."
318
+ msgstr ""
319
+
320
+ #: builds/amazon-s3-and-cloudfront/view/bucket-select.php:60
321
+ msgid "Create New Bucket"
322
+ msgstr ""
323
+
324
+ #: builds/amazon-s3-and-cloudfront/view/bucket-setting.php:7
325
+ msgid "Bucket"
326
+ msgstr ""
327
+
328
+ #: builds/amazon-s3-and-cloudfront/view/bucket-setting.php:11
329
+ msgid "Change"
330
+ msgstr ""
331
+
332
+ #: builds/amazon-s3-and-cloudfront/view/debug-info.php:2
333
+ msgid "Diagnostic Info"
334
+ msgstr ""
335
+
336
+ #: builds/amazon-s3-and-cloudfront/view/debug-info.php:11
337
+ msgctxt "Download to your computer"
338
+ msgid "Download"
339
+ msgstr ""
340
+
341
+ #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:5
342
+ msgid "Domain:"
343
+ msgstr ""
344
+
345
+ #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:23
346
+ msgid "Bucket name as subdomain"
347
+ msgstr ""
348
+
349
+ #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:28
350
+ msgid "Bucket name in path"
351
+ msgstr ""
352
+
353
+ #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:33
354
+ msgid "Bucket name as domain"
355
+ msgstr ""
356
+
357
+ #: builds/amazon-s3-and-cloudfront/view/domain-setting.php:38
358
+ msgid "CloudFront or custom domain"
359
+ msgstr ""
360
+
361
+ #: builds/amazon-s3-and-cloudfront/view/error-access.php:4
362
+ msgid "Access Denied to Bucket"
363
+ msgstr ""
364
+
365
+ #: builds/amazon-s3-and-cloudfront/view/error-access.php:6
366
+ #, php-format
367
+ msgid ""
368
+ "This could indicate your S3 policy is Read-Only. You need to go to <a href="
369
+ "\"%s\">Identity and Access Management</a> in your AWS console and manage the "
370
+ "policy for the user you're using for this plugin. Your policy should look "
371
+ "something like the following:"
372
+ msgstr ""
373
+
374
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:10
375
+ msgid "Settings saved."
376
+ msgstr ""
377
+
378
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:44
379
+ msgid "Enable/Disable the Plugin"
380
+ msgstr ""
381
+
382
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:51
383
+ msgid "Copy Files to S3"
384
+ msgstr ""
385
+
386
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:52
387
+ msgid ""
388
+ "When a file is uploaded to the Media Library, copy it to S3. Existing files "
389
+ "are <em>not</em> copied to S3."
390
+ msgstr ""
391
+
392
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:60
393
+ msgid "Rewrite File URLs"
394
+ msgstr ""
395
+
396
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:61
397
+ msgid ""
398
+ "For Media Library files that have been copied to S3, rewrite the URLs so "
399
+ "that they are served from S3/CloudFront instead of your server."
400
+ msgstr ""
401
+
402
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:65
403
+ msgid "Configure File URLs"
404
+ msgstr ""
405
+
406
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:83
407
+ msgid "Path"
408
+ msgstr ""
409
+
410
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:85
411
+ msgid "By default the path is the same as your local WordPress files:"
412
+ msgstr ""
413
+
414
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:98
415
+ msgid "Year/Month"
416
+ msgstr ""
417
+
418
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:100
419
+ msgid "Add the Year/Month in the URL."
420
+ msgstr ""
421
+
422
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:106
423
+ msgid "SSL:"
424
+ msgstr ""
425
+
426
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:114
427
+ msgid "Same as request"
428
+ msgstr ""
429
+
430
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:115
431
+ msgid "When the request is https://, use https:// for the file URL as well."
432
+ msgstr ""
433
+
434
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:119
435
+ msgid "Always SSL"
436
+ msgstr ""
437
+
438
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:120
439
+ msgid "Forces https:// to be used."
440
+ msgstr ""
441
+
442
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:121
443
+ msgid ""
444
+ "You cannot use the \"Bucket as a subdomain\" domain option when using SSL."
445
+ msgstr ""
446
+
447
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:125
448
+ msgid "Always non-SSL"
449
+ msgstr ""
450
+
451
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:126
452
+ msgid "Forces http:// to be used."
453
+ msgstr ""
454
+
455
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:132
456
+ msgid "Advanced Options"
457
+ msgstr ""
458
+
459
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:139
460
+ msgid "Remove Files From Server"
461
+ msgstr ""
462
+
463
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:140
464
+ msgid "Once a file has been copied to S3, remove it from the local server."
465
+ msgstr ""
466
+
467
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:148
468
+ msgid "Object Versioning"
469
+ msgstr ""
470
+
471
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:150
472
+ msgid ""
473
+ "Append a timestamp to the S3 file path. Recommended when using CloudFront so "
474
+ "you don't have to worry about cache invalidation."
475
+ msgstr ""
476
+
477
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:152
478
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:165
479
+ msgid "More info"
480
+ msgstr ""
481
+
482
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:162
483
+ msgid "Far Future Expiration Header"
484
+ msgstr ""
485
+
486
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:163
487
+ msgid ""
488
+ "Implements a \"Never Expire\" caching policy for browsers by setting an "
489
+ "Expires header for 10 years in the future. Should be used in conjunction "
490
+ "with object versioning above."
491
+ msgstr ""
492
+
493
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:175
494
+ msgid "Copy HiDPI (@2x) Images"
495
+ msgstr ""
496
+
497
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:176
498
+ #, php-format
499
+ msgid ""
500
+ "When uploading a file to S3, checks if there's a file of the same name with "
501
+ "an @2x suffix and copies it to S3 as well. Works with the <a href=\"%s\">WP "
502
+ "Retina 2x</a> plugin."
503
+ msgstr ""
504
+
505
+ #: builds/amazon-s3-and-cloudfront/view/settings.php:182
506
+ msgid "Save Changes"
507
+ msgstr ""
508
+
509
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:6
510
+ msgid "Pro Version?"
511
+ msgstr ""
512
+
513
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:11
514
+ msgid ""
515
+ "We're working on a pro version that will include the following features:"
516
+ msgstr ""
517
+
518
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:15
519
+ msgid "Copy existing Media Library to S3"
520
+ msgstr ""
521
+
522
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:16
523
+ msgid "Serve theme JS & CSS from S3/CloudFront"
524
+ msgstr ""
525
+
526
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:17
527
+ msgid ""
528
+ "WooCommerce & <abbr title=\"Easy Digital Downloads\">EDD</abbr> integration"
529
+ msgstr ""
530
+
531
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:18
532
+ msgid "Awesome email support"
533
+ msgstr ""
534
+
535
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:22
536
+ msgid "Your Email"
537
+ msgstr ""
538
+
539
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:26
540
+ msgid "First Name"
541
+ msgstr ""
542
+
543
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:30
544
+ msgid "Last Name"
545
+ msgstr ""
546
+
547
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:37
548
+ msgid "Send me news about a pro version"
549
+ msgstr ""
550
+
551
+ #: builds/amazon-s3-and-cloudfront/view/sidebar.php:41
552
+ msgid ""
553
+ "We promise we will not use your email for anything else and you can "
554
+ "unsubscribe with 1-click anytime."
555
+ msgstr ""
556
+
557
+ #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:2
558
+ msgid "As this is a free plugin, we do not provide support."
559
+ msgstr ""
560
+
561
+ #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:4
562
+ #, php-format
563
+ msgid ""
564
+ "You may ask the WordPress community for help by posting to the <a href=\"%s"
565
+ "\">WordPress.org support forum</a>. Response time can range from a few days "
566
+ "to a few weeks and will likely be from a non-developer."
567
+ msgstr ""
568
+
569
+ #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:6
570
+ #, php-format
571
+ msgid ""
572
+ "If you want a <strong>timely response via email from a developer</strong> "
573
+ "who works on this plugin, <a href=\"%s\">upgrade</a> and send us an email."
574
+ msgstr ""
575
+
576
+ #: builds/amazon-s3-and-cloudfront/view/wordpress-org-support.php:8
577
+ #, php-format
578
+ msgid ""
579
+ "If you've found a bug, please <a href=\"%s\">submit an issue on GitHub</a>."
580
+ msgstr ""
languages/amazon-s3-and-cloudfront.pot DELETED
@@ -1,457 +0,0 @@
1
- msgid ""
2
- msgstr ""
3
- "Project-Id-Version: Amazon S3 and CloudFront\n"
4
- "POT-Creation-Date: 2015-01-29 12:40-0000\n"
5
- "PO-Revision-Date: 2015-01-29 12:40-0000\n"
6
- "Last-Translator: Delicious Brains <nom@deliciousbrains.com>\n"
7
- "Language-Team: Delicious Brains <nom@deliciousbrains.com>\n"
8
- "Language: en\n"
9
- "MIME-Version: 1.0\n"
10
- "Content-Type: text/plain; charset=UTF-8\n"
11
- "Content-Transfer-Encoding: 8bit\n"
12
- "X-Generator: Poedit 1.7\n"
13
- "X-Poedit-Basepath: ..\n"
14
- "X-Poedit-SourceCharset: UTF-8\n"
15
- "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
16
- "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
17
- "_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
18
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
- "X-Poedit-SearchPath-0: .\n"
20
- "X-Poedit-SearchPathExcluded-0: vendor\n"
21
- "X-Poedit-SearchPathExcluded-1: assets\n"
22
-
23
- #: classes/amazon-s3-and-cloudfront.php:38
24
- msgid "Amazon S3 and CloudFront"
25
- msgstr ""
26
-
27
- #: classes/amazon-s3-and-cloudfront.php:39
28
- msgid "S3 and CloudFront"
29
- msgstr ""
30
-
31
- #: classes/amazon-s3-and-cloudfront.php:370
32
- #, php-format
33
- msgid "Mime type %s is not allowed"
34
- msgstr ""
35
-
36
- #: classes/amazon-s3-and-cloudfront.php:457
37
- #, php-format
38
- msgid "Error uploading %s to S3: %s"
39
- msgstr ""
40
-
41
- #: classes/amazon-s3-and-cloudfront.php:961
42
- msgid "There was an error attempting to access the file system"
43
- msgstr ""
44
-
45
- #: classes/amazon-s3-and-cloudfront.php:1001
46
- msgid "Cheatin&#8217; eh?"
47
- msgstr ""
48
-
49
- #: classes/amazon-s3-and-cloudfront.php:1005
50
- msgid "You do not have sufficient permissions to access this page."
51
- msgstr ""
52
-
53
- #: classes/amazon-s3-and-cloudfront.php:1011
54
- msgid "No bucket name provided."
55
- msgstr ""
56
-
57
- #: classes/amazon-s3-and-cloudfront.php:1037
58
- #: classes/amazon-s3-and-cloudfront.php:1070
59
- #: classes/amazon-s3-and-cloudfront.php:1129
60
- msgid "Failed to retrieve bucket region."
61
- msgstr ""
62
-
63
- #: classes/amazon-s3-and-cloudfront.php:1179
64
- #, php-format
65
- msgid "There was an error attempting to get the region of the bucket %s: %s"
66
- msgstr ""
67
-
68
- #: classes/amazon-s3-and-cloudfront.php:1291
69
- msgid ""
70
- "This is a test file to check if the user has write permission to S3. Delete "
71
- "me if found."
72
- msgstr ""
73
-
74
- #: classes/amazon-s3-and-cloudfront.php:1336
75
- msgid "Bucket Name:"
76
- msgstr ""
77
-
78
- #: classes/amazon-s3-and-cloudfront.php:1337
79
- msgid "Error creating bucket: "
80
- msgstr ""
81
-
82
- #: classes/amazon-s3-and-cloudfront.php:1340
83
- msgid "Error fetching buckets: "
84
- msgstr ""
85
-
86
- #: classes/amazon-s3-and-cloudfront.php:1342
87
- msgid "Error saving bucket: "
88
- msgstr ""
89
-
90
- #: classes/amazon-s3-and-cloudfront.php:1345
91
- msgid "Error getting URL preview: "
92
- msgstr ""
93
-
94
- #: classes/amazon-s3-and-cloudfront.php:1346
95
- msgid "The changes you made will be lost if you navigate away from this page"
96
- msgstr ""
97
-
98
- #: classes/amazon-s3-and-cloudfront.php:1358
99
- msgid "Cheatin' eh?"
100
- msgstr ""
101
-
102
- #: classes/as3cf-compatibility-check.php:25
103
- msgid ""
104
- "You can deactivate the Amazon S3 and CloudFront plugin to get rid of this "
105
- "notice."
106
- msgstr ""
107
-
108
- #: classes/as3cf-compatibility-check.php:28
109
- #, php-format
110
- msgid ""
111
- "Amazon S3 and CloudFront has been disabled as it requires the <a style="
112
- "\"text-decoration:none;\" href=\"%s\">Amazon&nbsp;Web&nbsp;Services</a> "
113
- "plugin."
114
- msgstr ""
115
-
116
- #: classes/as3cf-compatibility-check.php:31
117
- msgid "It appears to be installed already."
118
- msgstr ""
119
-
120
- #: classes/as3cf-compatibility-check.php:33
121
- msgctxt "Activate plugin"
122
- msgid "Activate it now"
123
- msgstr ""
124
-
125
- #: classes/as3cf-compatibility-check.php:37
126
- #, php-format
127
- msgid "<a href=\"%s\">Install it</a> and activate."
128
- msgstr ""
129
-
130
- #: classes/as3cf-compatibility-check.php:48
131
- #, php-format
132
- msgid ""
133
- "Amazon S3 and CloudFront has been disabled as it requires version %s or "
134
- "later of the <a style=\"text-decoration:none;\" href=\"%s\">Amazon&nbsp;"
135
- "Web&nbsp;Services</a> plugin."
136
- msgstr ""
137
-
138
- #: classes/as3cf-compatibility-check.php:51
139
- #, php-format
140
- msgid "You currently have version %s installed."
141
- msgstr ""
142
-
143
- #: classes/as3cf-compatibility-check.php:55
144
- msgid "Update to the latest version"
145
- msgstr ""
146
-
147
- #: classes/as3cf-compatibility-check.php:66
148
- #, php-format
149
- msgid ""
150
- "Amazon S3 and CloudFront has been disabled because it will not work with the "
151
- "version of the Amazon&nbsp;Web&nbsp;Services plugin installed. Amazon&nbsp;"
152
- "S3&nbsp;and&nbsp;CloudFront %s or later is required."
153
- msgstr ""
154
-
155
- #: classes/as3cf-compatibility-check.php:70
156
- msgid "Update Amazon S3 and CloudFront to the latest version"
157
- msgstr ""
158
-
159
- #: classes/as3cf-upgrade.php:95
160
- #, php-format
161
- msgid ""
162
- "<strong>Running Metadata Update</strong> &mdash; We&#8217;re going through "
163
- "all the Media Library items uploaded to S3 and updating the metadata with "
164
- "the bucket region it is served from. This will allow us to serve your files "
165
- "from the proper S3 region subdomain <span style=\"white-space:nowrap;\">(e."
166
- "g. s3-us-west-2.amazonaws.com)</span>. This will be done quietly in the "
167
- "background, processing a small batch of Media Library items every %d "
168
- "minutes. There should be no noticeable impact on your server&#8217;s "
169
- "performance."
170
- msgstr ""
171
-
172
- #: classes/as3cf-upgrade.php:96
173
- msgid "Pause Update"
174
- msgstr ""
175
-
176
- #: classes/as3cf-upgrade.php:100
177
- msgid ""
178
- "<strong>Metadata Update Paused</strong> &mdash; Updating Media Library "
179
- "metadata has been paused."
180
- msgstr ""
181
-
182
- #: classes/as3cf-upgrade.php:101
183
- msgid "Restart Update"
184
- msgstr ""
185
-
186
- #: classes/as3cf-upgrade.php:104
187
- msgid ""
188
- "<strong>Error Updating Metadata</strong> &mdash; We ran into some errors "
189
- "attempting to update the metadata for all your Media Library items that have "
190
- "been uploaded to S3. Please check your error log for details."
191
- msgstr ""
192
-
193
- #: classes/as3cf-upgrade.php:105
194
- msgid "Try Run It Again"
195
- msgstr ""
196
-
197
- #: classes/as3cf-upgrade.php:168
198
- #, php-format
199
- msgid "Every %d Minutes"
200
- msgstr ""
201
-
202
- #: view/settings.php:19
203
- msgid "Settings saved."
204
- msgstr ""
205
-
206
- #: view/settings.php:35
207
- msgid "S3 Policy is Read-Only"
208
- msgstr ""
209
-
210
- #: view/settings.php:37
211
- #, php-format
212
- msgid ""
213
- "You need to go to <a href=\"%s\">Identity and Access Management</a> in your "
214
- "AWS console and manage the policy for the user you're using for this plugin. "
215
- "Your policy should look something like the following:"
216
- msgstr ""
217
-
218
- #: view/settings.php:52
219
- msgid "Select an existing S3 bucket to use:"
220
- msgstr ""
221
-
222
- #: view/settings.php:55
223
- msgid "Cancel"
224
- msgstr ""
225
-
226
- #: view/settings.php:57
227
- msgid "Refresh"
228
- msgstr ""
229
-
230
- #: view/settings.php:60
231
- msgid ""
232
- "You can enter a bucket manually to avoid listing the buckets available. This "
233
- "can be helpful if you have an IAM policy that does not allow bucket listing "
234
- "or you have a large amount of buckets to load."
235
- msgstr ""
236
-
237
- #: view/settings.php:63
238
- msgid "Enter Bucket"
239
- msgstr ""
240
-
241
- #: view/settings.php:64
242
- msgid "Select Bucket"
243
- msgstr ""
244
-
245
- #: view/settings.php:68 view/settings.php:79
246
- msgid "Bucket Name"
247
- msgstr ""
248
-
249
- #: view/settings.php:69
250
- msgid "Saving..."
251
- msgstr ""
252
-
253
- #: view/settings.php:69
254
- msgid "Save"
255
- msgstr ""
256
-
257
- #: view/settings.php:73
258
- msgid "Loading..."
259
- msgstr ""
260
-
261
- #: view/settings.php:76
262
- msgid "Or create a new bucket:"
263
- msgstr ""
264
-
265
- #: view/settings.php:80
266
- msgid "Creating..."
267
- msgstr ""
268
-
269
- #: view/settings.php:80
270
- msgid "Create"
271
- msgstr ""
272
-
273
- #: view/settings.php:91
274
- msgid "Bucket"
275
- msgstr ""
276
-
277
- #: view/settings.php:95
278
- msgid "Change"
279
- msgstr ""
280
-
281
- #: view/settings.php:102
282
- msgid "Enable/Disable the Plugin"
283
- msgstr ""
284
-
285
- #: view/settings.php:109
286
- msgid "Copy Files to S3"
287
- msgstr ""
288
-
289
- #: view/settings.php:110
290
- msgid ""
291
- "When a file is uploaded to the Media Library, copy it to S3. Existing files "
292
- "are <em>not</em> copied to S3."
293
- msgstr ""
294
-
295
- #: view/settings.php:118
296
- msgid "Rewrite File URLs"
297
- msgstr ""
298
-
299
- #: view/settings.php:119
300
- msgid ""
301
- "For Media Library files that have been copied to S3, rewrite the URLs so "
302
- "that they are served from S3/CloudFront instead of your server."
303
- msgstr ""
304
-
305
- #: view/settings.php:123
306
- msgid "Configure File URLs"
307
- msgstr ""
308
-
309
- #: view/settings.php:137
310
- msgid "Domain:"
311
- msgstr ""
312
-
313
- #: view/settings.php:183
314
- msgid "Path"
315
- msgstr ""
316
-
317
- #: view/settings.php:185
318
- msgid "By default the path is the same as your local WordPress files:"
319
- msgstr ""
320
-
321
- #: view/settings.php:198
322
- msgid "Year/Month"
323
- msgstr ""
324
-
325
- #: view/settings.php:200
326
- msgid "Add the Year/Month in the URL."
327
- msgstr ""
328
-
329
- #: view/settings.php:206
330
- msgid "SSL"
331
- msgstr ""
332
-
333
- #: view/settings.php:214
334
- msgid "Same as request"
335
- msgstr ""
336
-
337
- #: view/settings.php:215
338
- msgid "When the request is https://, use https:// for the file URL as well."
339
- msgstr ""
340
-
341
- #: view/settings.php:219
342
- msgid "Always SSL"
343
- msgstr ""
344
-
345
- #: view/settings.php:220
346
- msgid "Forces https:// to be used."
347
- msgstr ""
348
-
349
- #: view/settings.php:221
350
- msgid ""
351
- "You cannot use the \"Bucket as a subdomain\" domain option when using SSL."
352
- msgstr ""
353
-
354
- #: view/settings.php:225
355
- msgid "Always non-SSL"
356
- msgstr ""
357
-
358
- #: view/settings.php:226
359
- msgid "Forces http:// to be used."
360
- msgstr ""
361
-
362
- #: view/settings.php:232
363
- msgid "Advanced Options"
364
- msgstr ""
365
-
366
- #: view/settings.php:239
367
- msgid "Remove Files From Server"
368
- msgstr ""
369
-
370
- #: view/settings.php:240
371
- msgid "Once a file has been copied to S3, remove it from the local server."
372
- msgstr ""
373
-
374
- #: view/settings.php:248
375
- msgid "Object Versioning"
376
- msgstr ""
377
-
378
- #: view/settings.php:250
379
- msgid ""
380
- "Append a timestamp to the S3 file path. Recommended when using CloudFront so "
381
- "you don't have to worry about cache invalidation."
382
- msgstr ""
383
-
384
- #: view/settings.php:252 view/settings.php:265
385
- msgid "More info"
386
- msgstr ""
387
-
388
- #: view/settings.php:262
389
- msgid "Far Future Expiration Header"
390
- msgstr ""
391
-
392
- #: view/settings.php:263
393
- msgid ""
394
- "Implements a \"Never Expire\" caching policy for browsers by setting an "
395
- "Expires header for 10 years in the future. Should be used in conjunction "
396
- "with object versioning above."
397
- msgstr ""
398
-
399
- #: view/settings.php:275
400
- msgid "Copy HiDPI (@2x) Images"
401
- msgstr ""
402
-
403
- #: view/settings.php:276
404
- #, php-format
405
- msgid ""
406
- "When uploading a file to S3, checks if there's a file of the same name with "
407
- "an @2x suffix and copies it to S3 as well. Works with the <a href=\"%s\">WP "
408
- "Retina 2x</a> plugin."
409
- msgstr ""
410
-
411
- #: view/settings.php:282
412
- msgid "Save Changes"
413
- msgstr ""
414
-
415
- #: view/sidebar.php:6
416
- msgid "Pro Version?"
417
- msgstr ""
418
-
419
- #: view/sidebar.php:11
420
- msgid ""
421
- "We're working on a pro version that will include the following features:"
422
- msgstr ""
423
-
424
- #: view/sidebar.php:15
425
- msgid "Copy existing Media Library to S3"
426
- msgstr ""
427
-
428
- #: view/sidebar.php:16
429
- msgid "Serve theme JS & CSS from S3/CloudFront"
430
- msgstr ""
431
-
432
- #: view/sidebar.php:17
433
- msgid ""
434
- "WooCommerce & <abbr title=\"Easy Digital Downloads\">EDD</abbr> integration"
435
- msgstr ""
436
-
437
- #: view/sidebar.php:18
438
- msgid "Awesome email support"
439
- msgstr ""
440
-
441
- #: view/sidebar.php:22
442
- msgid "Your Name"
443
- msgstr ""
444
-
445
- #: view/sidebar.php:26
446
- msgid "Your Email"
447
- msgstr ""
448
-
449
- #: view/sidebar.php:30
450
- msgid "Send me news about a pro version"
451
- msgstr ""
452
-
453
- #: view/sidebar.php:34
454
- msgid ""
455
- "We promise we will not use your email for anything else and you can "
456
- "unsubscribe with 1-click anytime."
457
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
- === Amazon S3 and Cloudfront ===
2
  Contributors: bradt
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5VPMGLLK94XJC
4
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  Requires at least: 3.5
6
- Tested up to: 4.2
7
- Stable tag: 0.8.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.
@@ -59,6 +59,36 @@ This version requires PHP 5.3.3+ and the Amazon Web Services plugin
59
 
60
  == Changelog ==
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  = 0.8.2 - 2015-01-31 =
63
  * New: Input bucket in settings to avoid listing all buckets
64
  * New: Specify bucket with 'AS3CF_BUCKET' constant
1
+ === WP Offload S3 ===
2
  Contributors: bradt
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5VPMGLLK94XJC
4
  Tags: uploads, amazon, s3, mirror, admin, media, cdn, cloudfront
5
  Requires at least: 3.5
6
+ Tested up to: 4.2.2
7
+ Stable tag: 0.9
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.
59
 
60
  == Changelog ==
61
 
62
+ = 0.9 - 2015-07-08 =
63
+ * New: Plugin rebranded to WP Offload S3
64
+ * New: Support tab added to _Offload S3_ screen containing diagnostic information
65
+ * New: Compatibility with the [Media Replace](https://wordpress.org/plugins/enable-media-replace/) plugin
66
+ * New: Select bucket region when creating a new bucket
67
+ * New: Toggle switches redesigned
68
+ * Improvement: Compatibility with release candidate of Pro plugin
69
+ * Improvement: Example IAM policy more secure
70
+ * Improvement: Set default bucket region using the `AS3CF_REGION` constant
71
+ * Improvement: Added `as3cf_object_meta` filter for developers
72
+ * Improvement: Bucket selection moved to modal window
73
+ * Improvement: Don't allow bucket names to contain invalid characters on creation
74
+ * Improvement: More verbose error messages on bucket selection
75
+ * Improvement: Settings link added to plugin row on _Plugins_ screen
76
+ * Improvement: Object versioning enabled by default
77
+ * Improvement: Uninstall routines added
78
+ * Improvement: JavaScript coding standards
79
+ * Improvement: Cache result when checking S3 bucket permissions
80
+ * Bug fix: Bucket region errors result in blank WP Offload S3 screen
81
+ * Bug fix: Editing an image when _Remove Files From Server_ option is enabled results in error
82
+ * Bug fix: Metadata upgrade procedure triggered on new installs
83
+ * Bug fix: File URLs when uploaded to a subdirectory result in incorrect S3 URLs
84
+ * Bug fix: Errors logged when trying to delete non-existent HiDPI images
85
+ * Bug fix: SignatureDoesNotMatch errors on regions with v4 authentication
86
+ * Bug fix: Customizer background image not editable
87
+ * Bug fix: Error when creating buckets with US Standard region
88
+ * Bug fix: Notices appearing incorrectly on some admin screens
89
+ * Bug fix: Subsite upload paths repeated on multisite installs
90
+ * Bug fix: Handle multisite installs where `BLOG_ID_CURRENT_SITE` is not 1
91
+
92
  = 0.8.2 - 2015-01-31 =
93
  * New: Input bucket in settings to avoid listing all buckets
94
  * New: Specify bucket with 'AS3CF_BUCKET' constant
uninstall.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Uninstall WP Offload S3
4
+ *
5
+ * @package amazon-s3-and-cloudfront
6
+ * @subpackage uninstall
7
+ * @copyright Copyright (c) 2015, Delicious Brains
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
14
+ exit;
15
+ }
16
+
17
+ require dirname( __FILE__ ) . '/classes/wp-aws-uninstall.php';
18
+
19
+ $options = 'tantan_wordpress_s3';
20
+ $postmeta = 'amazonS3_info';
21
+ $crons = 'as3cf_cron_update_meta_with_region';
22
+ $transients = array(
23
+ 'as3cf_notices',
24
+ 'as3cf_bucket_writable',
25
+ );
26
+
27
+ $as3cf_uninstall = new WP_AWS_Uninstall( $options, $postmeta, $crons, $transients );
view/bucket-select.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="as3cf-bucket-container <?php echo $prefix ;?>">
2
+ <div class="as3cf-bucket-manual">
3
+ <h3 data-modal-title="<?php _e( 'Change bucket', 'as3cf' ); ?>"><?php _e( 'What bucket would you like to use?', 'as3cf' ); ?></h3>
4
+ <form method="post" class="as3cf-manual-save-bucket-form">
5
+ <input type="text" class="as3cf-bucket-name" name="bucket_name" placeholder="<?php _e( 'Existing bucket name', 'as3cf' ); ?>" value="<?php echo $selected_bucket; ?>">
6
+ <p class="bucket-actions actions manual">
7
+ <button type="submit" class="bucket-action-save button button-primary" data-working="<?php _e( 'Saving...', 'as3cf' ); ?>"><?php _e( 'Save Bucket', 'as3cf' ); ?></button>
8
+ <span><a href="#" class="bucket-action-browse"><?php _e( 'Browse existing buckets', 'as3cf' ); ?></a></span>
9
+ <span><a href="#" class="bucket-action-create"><?php _e( 'Create new bucket', 'as3cf' ); ?></a></span>
10
+ </p>
11
+ <p class="bucket-actions actions select">
12
+ <button type="submit" class="bucket-action-save button button-primary" data-working="<?php _e( 'Saving...', 'as3cf' ); ?>"><?php _e( 'Save Bucket', 'as3cf' ); ?></button>
13
+ <span><a href="#" class="bucket-action-cancel"><?php _e( 'Cancel', 'as3cf' ); ?></a></span>
14
+ </p>
15
+ </form>
16
+ </div>
17
+ <div class="as3cf-bucket-select">
18
+ <h3><?php _e( 'Select bucket', 'as3cf' ); ?></h3>
19
+ <ul class="as3cf-bucket-list" data-working="<?php _e( 'Loading...', 'as3cf' ); ?>"></ul>
20
+ <p class="bucket-actions actions manual">
21
+ <span><a href="#" class="bucket-action-cancel"><?php _e( 'Cancel', 'as3cf' ); ?></a></span>
22
+ <span class="right"><a href="#" class="bucket-action-refresh"><?php _e( 'Refresh', 'as3cf' ); ?></a></span>
23
+ </p>
24
+ <p class="bucket-actions actions select">
25
+ <span><a href="#" class="bucket-action-manual"><?php _e( 'Enter bucket name', 'as3cf' ); ?></a></span>
26
+ <span><a href="#" class="bucket-action-create"><?php _e( 'Create new bucket', 'as3cf' ); ?></a></span>
27
+ <span class="right"><a href="#" class="bucket-action-refresh"><?php _e( 'Refresh', 'as3cf' ); ?></a></span>
28
+ </p>
29
+ </div>
30
+ <div class="as3cf-bucket-create">
31
+ <h3><?php _e( 'Create new bucket', 'as3cf' ); ?></h3>
32
+ <form method="post" class="as3cf-create-bucket-form">
33
+ <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
34
+ <table class="form-table">
35
+ <tr>
36
+ <td>
37
+ <?php _e( 'Bucket Name:', 'as3cf' ); ?>
38
+ </td>
39
+ <td>
40
+ <input type="text" class="as3cf-bucket-name" name="bucket_name" placeholder="<?php _e( 'Bucket Name', 'as3cf' ); ?>">
41
+ <p class="as3cf-invalid-bucket-name"></p>
42
+ </td>
43
+ </tr>
44
+ <?php if ( ! defined( 'AS3CF_REGION' ) ) : ?>
45
+ <tr>
46
+ <td>
47
+ <?php _e( 'Region:', 'as3cf' ); ?>
48
+ </td>
49
+ <td>
50
+ <select class="bucket-create-region" name="region_name">
51
+ <?php foreach ( $this->get_aws_regions() as $value => $label ) : ?>
52
+ <option value="<?php echo $value; ?>"> <?php echo $label; ?></option>
53
+ <?php endforeach; ?>
54
+ </select>
55
+ </td>
56
+ </tr>
57
+ <?php endif; ?>
58
+ </table>
59
+ <p class="bucket-actions actions">
60
+ <button type="submit" class="button button-primary" data-working="<?php _e( 'Creating...', 'as3cf' ); ?>"><?php _e( 'Create New Bucket', 'as3cf' ); ?></button>
61
+ <span><a href="#" class="bucket-action-cancel"><?php _e( 'Cancel', 'as3cf' ); ?></a></span>
62
+ </p>
63
+ </form>
64
+ </div>
65
+ </div>
view/bucket-setting.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $constant = strtoupper( str_replace( '-', '_', $prefix ) . '_BUCKET' );
3
+ $tr_class = ( isset( $tr_class ) ) ? $tr_class : '';
4
+ ?>
5
+
6
+ <tr class="<?php echo $tr_class; ?>">
7
+ <td><h3><?php _e( 'Bucket', 'as3cf' ); ?></h3></td>
8
+ <td>
9
+ <span id="<?php echo $prefix; ?>-active-bucket" class="as3cf-active-bucket"><?php echo $selected_bucket; // xss ok ?></span>
10
+ <?php if ( ! defined( $constant ) ) : ?>
11
+ <a href="#" class="as3cf-change-bucket" data-as3cf-modal=".as3cf-bucket-container"><?php _e( 'Change', 'as3cf' ); ?></a>
12
+ <?php endif; ?>
13
+ <input id="<?php echo $prefix; ?>-bucket" type="hidden" class="no-compare" name="bucket" value="<?php echo esc_attr( $selected_bucket ); ?>">
14
+ <?php
15
+ $region = $this->get_setting( 'region' );
16
+ if ( is_wp_error( $region ) ) {
17
+ $region = '';
18
+ } ?>
19
+ <input id="<?php echo $prefix; ?>-region" type="hidden" class="no-compare" name="region" value="<?php echo esc_attr( $region ); ?>">
20
+ <?php $bucket_select = $this->get_setting( 'manual_bucket' ) ? 'manual' : ''; ?>
21
+ <input id="<?php echo $prefix; ?>-bucket-select" type="hidden" class="no-compare" value="<?php echo esc_attr( $bucket_select ); ?>">
22
+ <?php
23
+ if ( isset( $after_bucket_content ) ) {
24
+ echo $after_bucket_content;
25
+ }
26
+ ?>
27
+ </td>
28
+ </tr>
view/checkbox.php CHANGED
@@ -1,9 +1,9 @@
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" />
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 . $value ? ' on' : ''; ?>">
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" />
view/debug-info.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="debug support-section">
2
+ <h3><?php _e( 'Diagnostic Info', 'as3cf' ); ?></h3>
3
+ <textarea class="debug-log-textarea" autocomplete="off" readonly><?php $this->output_diagnostic_info(); ?></textarea>
4
+ <?php
5
+ $args = array(
6
+ 'nonce' => wp_create_nonce( 'as3cf-download-log' ),
7
+ 'as3cf-download-log' => '1',
8
+ 'hash' => 'support',
9
+ );
10
+ $url = $this->get_plugin_page_url( $args, 'network', false ); ?>
11
+ <a href="<?php echo esc_url( $url ); ?>" class="button"><?php _ex( 'Download', 'Download to your computer', 'as3cf' ); ?></a>
12
+ </div>
view/domain-setting.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $tr_class = ( isset( $tr_class ) ) ? $tr_class : ''; ?>
3
+ <tr class="<?php echo $tr_class; ?>">
4
+ <td>
5
+ <h4><?php _e( 'Domain:', 'as3cf' ) ?></h4>
6
+ </td>
7
+ <td>
8
+ <?php
9
+ $domain = $this->get_setting( 'domain' );
10
+ $subdomain_disabled = '';
11
+ $subdomain_class = '';
12
+ if ( 'https' == $this->get_setting( 'ssl' ) ) {
13
+ if ( 'subdomain' == $domain ) {
14
+ $domain = 'path';
15
+ }
16
+ $subdomain_disabled = 'disabled="disabled"';
17
+ $subdomain_class = 'disabled';
18
+ }
19
+ ?>
20
+ <div class="as3cf-domain as3cf-radio-group">
21
+ <label class="subdomain-wrap <?php echo $subdomain_class; // xss ok?>">
22
+ <input type="radio" name="domain" value="subdomain" <?php checked( $domain, 'subdomain' ); ?> <?php echo $subdomain_disabled; // xss ok ?>>
23
+ <?php _e( 'Bucket name as subdomain', 'as3cf' ); ?>
24
+ <p>http://bucket-name.s3.amazon.com/&hellip;</p>
25
+ </label>
26
+ <label>
27
+ <input type="radio" name="domain" value="path" <?php checked( $domain, 'path' ); ?>>
28
+ <?php _e( 'Bucket name in path', 'as3cf' ); ?>
29
+ <p>http://s3.amazon.com/bucket-name/&hellip;</p>
30
+ </label>
31
+ <label>
32
+ <input type="radio" name="domain" value="virtual-host" <?php checked( $domain, 'virtual-host' ); ?>>
33
+ <?php _e( 'Bucket name as domain', 'as3cf' ); ?>
34
+ <p>http://bucket-name/&hellip;</p>
35
+ </label>
36
+ <label>
37
+ <input id="cloudfront" type="radio" name="domain" value="cloudfront" <?php checked( $domain, 'cloudfront' ); ?>>
38
+ <?php _e( 'CloudFront or custom domain', 'as3cf' ); ?>
39
+ <p class="as3cf-setting cloudfront <?php echo ( 'cloudfront' == $domain ) ? '' : 'hide'; // xss ok ?>">
40
+ <input type="text" name="cloudfront" value="<?php echo esc_attr( $this->get_setting( 'cloudfront' ) ); ?>" size="30" />
41
+ </p>
42
+ </label>
43
+ </div>
44
+ </td>
45
+ </tr>
view/error-access.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="error inline as3cf-can-write-error as3cf-error" style="<?php echo ( $can_write ) ? 'display: none;' : ''; // xss ok ?>">
2
+ <p>
3
+ <strong>
4
+ <?php _e( 'Access Denied to Bucket', 'as3cf' ); ?>
5
+ </strong>&mdash;
6
+ <?php printf( __( 'This could indicate your S3 policy is Read-Only. 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' ); ?>
7
+ </p>
8
+ <pre><code>{
9
+ "Statement": [
10
+ {
11
+ "Effect": "Allow",
12
+ "Action": [
13
+ "s3:CreateBucket",
14
+ "s3:DeleteObject",
15
+ "s3:Put*",
16
+ "s3:Get*",
17
+ "s3:List*"
18
+ ],
19
+ "Resource": [
20
+ "arn:aws:s3:::*"
21
+ ]
22
+ }
23
+ ]
24
+ }</code></pre>
25
+ </div>
view/error-fatal.php CHANGED
@@ -1,5 +1,5 @@
1
- <div class="aws-content as3cf-error">
2
-
3
- <?php $this->render_view( 'error', compact( 'message' ) ); ?>
4
-
5
  </div>
1
+ <div class="aws-content as3cf-error fatal">
2
+ <div class="error inline as3cf-error">
3
+ <p><?php echo $message; // xss ok ?></p>
4
+ </div>
5
  </div>
view/error.php DELETED
@@ -1,3 +0,0 @@
1
- <div class="error">
2
- <p><?php echo $message; // xss ok ?></p>
3
- </div>
 
 
 
view/notice.php CHANGED
@@ -1,3 +1,8 @@
1
- <div class="updated as3cf-notice">
 
 
 
 
 
2
  <p><?php echo $message; // xss ok ?></p>
3
- </div>
1
+ <?php
2
+ $type = ( isset( $type ) ) ? $type : 'notice-info';
3
+ $dismissible = ( isset( $dismissible ) ) ? $dismissible : false;
4
+ $inline = ( isset( $inline ) ) ? $inline : false;
5
+ ?>
6
+ <div class="notice <?php echo $type; ?><?php echo ( $dismissible ) ? ' is-dismissible' : ''; ?> as3cf-notice <?php echo ( $inline ) ? ' inline' : ''; ?>">
7
  <p><?php echo $message; // xss ok ?></p>
8
+ </div>
view/settings-tabs.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <h2 class="nav-tab-wrapper">
2
+ <?php
3
+ $tabs = $this->get_settings_tabs();
4
+ foreach ( $tabs as $tab => $label ) : ?>
5
+ <a href="#" class="nav-tab <?php echo 'media' == $tab ? 'nav-tab-active' : ''; ?> js-action-link <?php echo $tab; ?>" data-tab="<?php echo $tab; ?>">
6
+ <?php echo esc_html( $label ); ?>
7
+ </a>
8
+ <?php endforeach; ?>
9
+ </h2>
view/settings.php CHANGED
@@ -1,104 +1,46 @@
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
- {
43
- "Effect": "Allow",
44
- "Action": "s3:*",
45
- "Resource": "*"
46
- }
47
- ]
48
- }</code></pre>
49
- </div>
50
- <?php $bucket_toggle_class = $this->get_setting( 'manual_bucket' ) ? 'manual' : ''; ?>
51
- <div class="as3cf-bucket-select <?php echo $bucket_toggle_class; ?>">
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
- <p>
60
- <?php _e( 'You can enter a bucket manually to avoid listing the buckets available. This can be helpful if you have an IAM policy that does not allow bucket listing or you have a large amount of buckets to load.', 'as3cf' ); ?>
61
- </p>
62
- <p>
63
- <a href="#" class="as3cf-manual-bucket-toggle"> <?php _e( 'Enter Bucket', 'as3cf' ); ?></a>
64
- <a href="#" class="as3cf-bucket-list-toggle"><?php _e( 'Select Bucket', 'as3cf' ); ?></a>
65
- </p>
66
- <div class="as3cf-manual-save-bucket-wrapper">
67
- <form method="post" class="as3cf-manual-save-bucket-form">
68
- <input type="text" class="as3cf-bucket-name" name="bucket_name" placeholder="<?php _e( 'Bucket Name', 'as3cf' ); ?>" value="<?php echo $selected_bucket; ?>">
69
- <button type="submit" class="button" data-working="<?php _e( 'Saving...', 'as3cf' ); ?>"><?php _e( 'Save', 'as3cf' ); ?></button>
70
- </form>
71
- </div>
72
- <div class="as3cf-bucket-list-wrapper">
73
- <ul class="as3cf-bucket-list" data-working="<?php _e( 'Loading...', 'as3cf' ); ?>">
74
- </ul>
75
- </div>
76
- <h3><?php _e( 'Or create a new bucket:', 'as3cf' ); ?></h3>
77
- <form method="post" class="as3cf-create-bucket-form">
78
- <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
79
- <input type="text" class="as3cf-bucket-name" name="bucket_name" placeholder="<?php _e( 'Bucket Name', 'as3cf' ); ?>">
80
- <button type="submit" class="button" data-working="<?php _e( 'Creating...', 'as3cf' ); ?>"><?php _e( 'Create', 'as3cf' ); ?></button>
81
- </form>
82
- </div>
83
 
84
  <div class="as3cf-main-settings">
85
  <form method="post">
86
  <input type="hidden" name="action" value="save" />
87
- <?php wp_nonce_field( 'as3cf-save-settings' ) ?>
 
 
88
 
89
  <table class="form-table">
90
- <tr class="as3cf-border-bottom">
91
- <td><h3><?php _e( 'Bucket', 'as3cf' ); ?></h3></td>
92
- <td>
93
- <span class="as3cf-active-bucket"><?php echo $selected_bucket; // xss ok ?></span>
94
- <?php if ( ! defined( 'AS3CF_BUCKET' ) ) : ?>
95
- <a href="#" class="as3cf-change-bucket"><?php _e( 'Change', 'as3cf' ); ?></a>
96
- <?php endif; ?>
97
- <input id="as3cf-bucket" type="hidden" class="no-compare" name="bucket" value="<?php echo esc_attr( $selected_bucket ); ?>">
98
- <input id="as3cf-region" type="hidden" class="no-compare" name="region" value="<?php echo esc_attr( $this->get_setting( 'region' ) ); ?>">
99
- </td>
100
- </tr>
101
- <tr>
102
  <td colspan="2"><h3><?php _e( 'Enable/Disable the Plugin', 'as3cf' ); ?></h3></td>
103
  </tr>
104
  <tr>
@@ -119,7 +61,7 @@ if ( is_wp_error( $can_write ) ) {
119
  <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>
120
  </td>
121
  </tr>
122
- <tr class="configure-url">
123
  <td colspan="2"><h3><?php _e( 'Configure File URLs', 'as3cf' ); ?></h3></td>
124
  </tr>
125
  <tr class="configure-url">
@@ -132,49 +74,7 @@ if ( is_wp_error( $can_write ) ) {
132
  </div>
133
  </td>
134
  </tr>
135
- <tr class="configure-url url-preview">
136
- <td>
137
- <h4><?php _e( 'Domain:', 'as3cf' ) ?></h4>
138
- </td>
139
- <td>
140
- <?php
141
- $domain = $this->get_setting( 'domain' );
142
- $subdomain_disabled = '';
143
- $subdomain_class = '';
144
- if ( 'https' == $this->get_setting( 'ssl' ) ) {
145
- if ( 'subdomain' == $domain ) {
146
- $domain = 'path';
147
- }
148
- $subdomain_disabled = 'disabled="disabled"';
149
- $subdomain_class = 'disabled';
150
- }
151
- ?>
152
- <div class="as3cf-domain as3cf-radio-group">
153
- <label class="subdomain-wrap <?php echo $subdomain_class; // xss ok?>">
154
- <input type="radio" name="domain" value="subdomain" <?php checked( $domain, 'subdomain' ); ?> <?php echo $subdomain_disabled; // xss ok ?>>
155
- Bucket name as subdomain
156
- <p>http://bucket-name.s3.amazon.com/&hellip;</p>
157
- </label>
158
- <label>
159
- <input type="radio" name="domain" value="path" <?php checked( $domain, 'path' ); ?>>
160
- Bucket name in path
161
- <p>http://s3.amazon.com/bucket-name/&hellip;</p>
162
- </label>
163
- <label>
164
- <input type="radio" name="domain" value="virtual-host" <?php checked( $domain, 'virtual-host' ); ?>>
165
- Bucket name as domain
166
- <p>http://bucket-name/&hellip;</p>
167
- </label>
168
- <label>
169
- <input id="cloudfront" type="radio" name="domain" value="cloudfront" <?php checked( $domain, 'cloudfront' ); ?>>
170
- CloudFront or custom domain
171
- <p class="as3cf-setting cloudfront <?php echo ( 'cloudfront' == $domain ) ? '' : 'hide'; // xss ok ?>">
172
- <input type="text" name="cloudfront" value="<?php echo esc_attr( $this->get_setting( 'cloudfront' ) ); ?>" size="40" />
173
- </p>
174
- </label>
175
- </div>
176
- </td>
177
- </tr>
178
  <tr class="configure-url url-preview">
179
  <td>
180
  <?php $this->render_view( 'checkbox', array( 'key' => 'enable-object-prefix', 'class' => 'sub-toggle' ) ); ?>
@@ -182,7 +82,7 @@ if ( is_wp_error( $can_write ) ) {
182
  <td>
183
  <h4><?php _e( 'Path', 'as3cf' ) ?></h4>
184
  <p class="object-prefix-desc">
185
- <?php _e( 'By default the path is the same as your local WordPress files:' ); ?>
186
  <em><?php echo $this->get_default_object_prefix(); // xss ok ?></em>
187
  </p>
188
  <p class="as3cf-setting enable-object-prefix <?php echo ( $this->get_setting( 'enable-object-prefix' ) ) ? '' : 'hide'; // xss ok ?>">
@@ -203,7 +103,7 @@ if ( is_wp_error( $can_write ) ) {
203
  </tr>
204
  <tr class="configure-url as3cf-border-bottom url-preview">
205
  <td>
206
- <h4><?php _e( 'SSL', 'as3cf' ) ?></h4>
207
  </td>
208
  <td>
209
  <?php
@@ -228,7 +128,7 @@ if ( is_wp_error( $can_write ) ) {
228
  </div>
229
  </td>
230
  </tr>
231
- <tr class="advanced-options">
232
  <td colspan="2"><h3><?php _e( 'Advanced Options', 'as3cf' ); ?></h3></td>
233
  </tr>
234
  <tr class="advanced-options">
@@ -279,11 +179,20 @@ if ( is_wp_error( $can_write ) ) {
279
 
280
  </table>
281
  <p>
282
- <button type="submit" class="button button-primary"><?php _e( 'Save Changes', 'amazon-web-services' ); ?></button>
283
  </p>
284
  </form>
285
  </div>
286
 
287
- <?php $this->render_view( 'sidebar' ); ?>
 
 
 
288
 
289
- </div>
 
 
 
 
 
 
1
  <?php
2
+ $updated_class = '';
 
 
 
 
 
 
 
 
 
 
3
  if ( isset( $_GET['updated'] ) ) { // input var okay
4
  $updated_class = 'show';
5
  }
6
+ $prefix = $this->get_plugin_prefix_slug();
7
+ ?>
8
+ <div class="notice is-dismissible as3cf-updated updated inline <?php echo $updated_class; // xss ok ?>">
9
+ <p>
10
+ <?php _e( 'Settings saved.', 'as3cf' ); ?>
11
+ </p>
12
+ </div>
13
+ <?php
14
+ $selected_bucket = $this->get_setting( 'bucket' ); ?>
15
+ <div id="tab-media" data-prefix="as3cf" class="as3cf-tab aws-content<?php echo ( $selected_bucket ) ? ' as3cf-has-bucket' : ''; // xss ok ?>">
16
+ <div class="error inline as3cf-bucket-error as3cf-error" style="display: none;">
17
  <p>
18
+ <span class="title"></span>
19
+ <span class="message"></span>
20
  </p>
21
  </div>
22
 
23
  <?php
24
+ do_action( 'as3cf_media_pre_tab_render' );
25
+ $this->render_bucket_permission_errors(); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  <div class="as3cf-main-settings">
28
  <form method="post">
29
  <input type="hidden" name="action" value="save" />
30
+ <input type="hidden" name="plugin" value="<?php echo $this->get_plugin_slug(); ?>" />
31
+ <?php wp_nonce_field( $this->get_settings_nonce_key() ) ?>
32
+ <?php do_action( 'as3cf_form_hidden_fields' ); ?>
33
 
34
  <table class="form-table">
35
+ <?php
36
+ $this->render_view( 'bucket-setting',
37
+ array(
38
+ 'prefix' => $prefix,
39
+ 'selected_bucket' => $selected_bucket,
40
+ 'tr_class' => 'as3cf-border-bottom',
41
+ )
42
+ ); ?>
43
+ <tr class="as3cf-setting-title">
 
 
 
44
  <td colspan="2"><h3><?php _e( 'Enable/Disable the Plugin', 'as3cf' ); ?></h3></td>
45
  </tr>
46
  <tr>
61
  <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>
62
  </td>
63
  </tr>
64
+ <tr class="configure-url as3cf-setting-title">
65
  <td colspan="2"><h3><?php _e( 'Configure File URLs', 'as3cf' ); ?></h3></td>
66
  </tr>
67
  <tr class="configure-url">
74
  </div>
75
  </td>
76
  </tr>
77
+ <?php $this->render_view( 'domain-setting', array( 'tr_class' => 'configure-url url-preview' ) ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  <tr class="configure-url url-preview">
79
  <td>
80
  <?php $this->render_view( 'checkbox', array( 'key' => 'enable-object-prefix', 'class' => 'sub-toggle' ) ); ?>
82
  <td>
83
  <h4><?php _e( 'Path', 'as3cf' ) ?></h4>
84
  <p class="object-prefix-desc">
85
+ <?php _e( 'By default the path is the same as your local WordPress files:', 'as3cf' ); ?>
86
  <em><?php echo $this->get_default_object_prefix(); // xss ok ?></em>
87
  </p>
88
  <p class="as3cf-setting enable-object-prefix <?php echo ( $this->get_setting( 'enable-object-prefix' ) ) ? '' : 'hide'; // xss ok ?>">
103
  </tr>
104
  <tr class="configure-url as3cf-border-bottom url-preview">
105
  <td>
106
+ <h4><?php _e( 'SSL:', 'as3cf' ) ?></h4>
107
  </td>
108
  <td>
109
  <?php
128
  </div>
129
  </td>
130
  </tr>
131
+ <tr class="advanced-options as3cf-setting-title">
132
  <td colspan="2"><h3><?php _e( 'Advanced Options', 'as3cf' ); ?></h3></td>
133
  </tr>
134
  <tr class="advanced-options">
179
 
180
  </table>
181
  <p>
182
+ <button type="submit" class="button button-primary"><?php _e( 'Save Changes', 'as3cf' ); ?></button>
183
  </p>
184
  </form>
185
  </div>
186
 
187
+ <?php $this->render_view( 'bucket-select', array( 'prefix' => $prefix, 'selected_bucket' => $selected_bucket ) ); ?>
188
+ </div>
189
+
190
+ <?php $this->render_view( 'support' ); ?>
191
 
192
+ <?php do_action( 'as3cf_after_settings' ); ?>
193
+
194
+ <?php
195
+ if ( ! $this->is_pro() ) {
196
+ $this->render_view( 'sidebar' );
197
+ }
198
+ ?>
view/sidebar.php CHANGED
@@ -2,7 +2,7 @@
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>
7
 
8
  <?php $user = wp_get_current_user(); ?>
@@ -18,14 +18,21 @@
18
  <li><?php echo wptexturize( __( 'Awesome email support', 'as3cf' ) ); // xss ok ?></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">
26
- <input type="email" name="cm-dlihik-dlihik" value="<?php echo esc_attr( $user->user_email ); ?>" placeholder="<?php _e( 'Your Email', 'as3cf' ); ?>"/>
27
  </div>
28
 
 
 
 
 
 
 
 
29
  <div class="field submit-button">
30
  <input type="submit" class="button" value="<?php _e( 'Send me news about a pro version', 'as3cf' ); ?>"/>
31
  </div>
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.com/email-subscribe/" target="_blank" class="subscribe block">
6
  <h2><?php _e( 'Pro Version?', 'as3cf' ); ?></h2>
7
 
8
  <?php $user = wp_get_current_user(); ?>
18
  <li><?php echo wptexturize( __( 'Awesome email support', 'as3cf' ) ); // xss ok ?></li>
19
  </ul>
20
 
21
+ <div class="field">
22
+ <input type="email" name="email" value="<?php echo esc_attr( $user->user_email ); ?>" placeholder="<?php _e( 'Your Email', 'as3cf' ); ?>"/>
23
  </div>
24
 
25
+ <div class="field">
26
+ <input type="text" name="first_name" value="<?php echo esc_attr( trim( $user->first_name ) ); ?>" placeholder="<?php _e( 'First Name', 'as3cf' ); ?>"/>
27
  </div>
28
 
29
+ <div class="field">
30
+ <input type="text" name="last_name" value="<?php echo esc_attr( trim( $user->last_name ) ); ?>" placeholder="<?php _e( 'Last Name', 'as3cf' ); ?>"/>
31
+ </div>
32
+
33
+ <input type="hidden" name="campaigns[]" value="3" />
34
+ <input type="hidden" name="source" value="1" />
35
+
36
  <div class="field submit-button">
37
  <input type="submit" class="button" value="<?php _e( 'Send me news about a pro version', 'as3cf' ); ?>"/>
38
  </div>
view/support.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="tab-support" class="aws-content as3cf-tab">
2
+ <?php
3
+ if ( ! $this->is_pro() ) {
4
+ $this->render_view( 'wordpress-org-support' );
5
+ }
6
+
7
+ do_action( 'as3cf_support_pre_debug' );
8
+
9
+ $this->render_view( 'debug-info' );
10
+
11
+ do_action( 'as3cf_support_post_debug' );
12
+
13
+ ?>
14
+ </div>
view/wordpress-org-support.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <div class="support support-section">
2
+ <p><?php _e( 'As this is a free plugin, we do not provide support.', 'as3cf'); ?></p>
3
+
4
+ <p><?php printf( __( 'You may ask the WordPress community for help by posting to the <a href="%s">WordPress.org support forum</a>. Response time can range from a few days to a few weeks and will likely be from a non-developer.', 'as3cf'), 'https://wordpress.org/plugins/amazon-s3-and-cloudfront/' ); ?></p>
5
+
6
+ <p class="upgrade-to-pro"><?php printf( __( 'If you want a <strong>timely response via email from a developer</strong> who works on this plugin, <a href="%s">upgrade</a> and send us an email.', 'as3cf' ), 'https://deliciousbrains.com/wp-offload-s3/?utm_source=insideplugin&utm_medium=web&utm_content=support-tab&utm_campaign=freeplugin-as3cf' ); ?></p>
7
+
8
+ <p><?php printf( __( 'If you\'ve found a bug, please <a href="%s">submit an issue on GitHub</a>.', 'as3cf' ), 'https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/issues' ); ?></p>
9
+
10
+ </div>
wordpress-s3.php CHANGED
@@ -1,10 +1,10 @@
1
  <?php
2
  /*
3
- 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.8.2
8
  Author URI: http://bradt.ca
9
  Network: True
10
  Text Domain: as3cf
@@ -26,18 +26,24 @@ Domain Path: /languages/
26
  // Then completely rewritten.
27
  */
28
 
29
- $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['version'] = '0.8.2';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
- 'amazon-s3-and-cloudfront-edd' => '1.0.1',
33
- 'amazon-s3-and-cloudfront-pro' => '0.9',
34
  );
35
 
36
- $aws_plugin_version_required = '0.2.2';
37
 
38
- require dirname( __FILE__ ) . '/classes/as3cf-compatibility-check.php';
39
  global $as3cf_compat_check;
40
- $as3cf_compat_check = new AS3CF_Compatibility_Check( __FILE__, $aws_plugin_version_required );
 
 
 
 
 
 
 
41
 
42
  function as3cf_init( $aws ) {
43
  global $as3cf_compat_check;
@@ -49,6 +55,7 @@ function as3cf_init( $aws ) {
49
  $abspath = dirname( __FILE__ );
50
  require_once $abspath . '/include/functions.php';
51
  require_once $abspath . '/classes/as3cf-upgrade.php';
 
52
  require_once $abspath . '/classes/amazon-s3-and-cloudfront.php';
53
  $as3cf = new Amazon_S3_And_CloudFront( __FILE__, $aws );
54
  }
1
  <?php
2
  /*
3
+ Plugin Name: WP Offload S3
4
  Plugin URI: http://wordpress.org/extend/plugins/amazon-s3-and-cloudfront/
5
  Description: Automatically copies media uploads to Amazon S3 for storage and delivery. Optionally configure Amazon CloudFront for even faster delivery.
6
  Author: Brad Touesnard
7
+ Version: 0.9
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.9';
30
 
31
  $GLOBALS['aws_meta']['amazon-s3-and-cloudfront']['supported_addon_versions'] = array(
32
+ 'amazon-s3-and-cloudfront-pro' => '1.0',
 
33
  );
34
 
35
+ $aws_plugin_version_required = '0.3';
36
 
37
+ require dirname( __FILE__ ) . '/classes/wp-aws-compatibility-check.php';
38
  global $as3cf_compat_check;
39
+ $as3cf_compat_check = new WP_AWS_Compatibility_Check(
40
+ 'WP Offload S3',
41
+ 'amazon-s3-and-cloudfront',
42
+ __FILE__,
43
+ 'Amazon Web Services',
44
+ 'amazon-web-services',
45
+ $aws_plugin_version_required
46
+ );
47
 
48
  function as3cf_init( $aws ) {
49
  global $as3cf_compat_check;
55
  $abspath = dirname( __FILE__ );
56
  require_once $abspath . '/include/functions.php';
57
  require_once $abspath . '/classes/as3cf-upgrade.php';
58
+ require_once $abspath . '/classes/as3cf-plugin-compatibility.php';
59
  require_once $abspath . '/classes/amazon-s3-and-cloudfront.php';
60
  $as3cf = new Amazon_S3_And_CloudFront( __FILE__, $aws );
61
  }