CDN Enabler – WordPress CDN Plugin - Version 2.0.0

Version Description

  • Update output buffer timing to start earlier on the setup_theme hook instead of the template_redirect hook (#23)
  • Update settings (#23)
  • Update requirements check (#23)
  • Update purge CDN cache handling (#23)
  • Add new rewrite engine (#23)
  • Add WP-CLI cache purging (#23)
  • Add configuration validation (#23)
  • Add cdn_enabler_user_can_purge_cache, cdn_enabler_page_contents_before_rewrite, cdn_enabler_bypass_rewrite, cdn_enabler_site_hostnames, and cdn_enabler_rewrite_relative_urls filter hooks (#23)
  • Fix requirement notices being shown to all users (#23)
  • Fix rewriting limitations (#23)
  • Deprecate user_can_clear_cache filter hook in favor of replacement (#23)
Download this release

Release Info

Developer keycdn
Plugin Icon 128x128 CDN Enabler – WordPress CDN Plugin
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.0.9 to 2.0.0

cdn-enabler.php CHANGED
@@ -1,85 +1,65 @@
1
  <?php
2
  /*
3
- Plugin Name: CDN Enabler
4
- Text Domain: cdn-enabler
5
- Description: Simply integrate a Content Delivery Network (CDN) into your WordPress site.
6
- Author: KeyCDN
7
- Author URI: https://www.keycdn.com
8
- License: GPLv2 or later
9
- Version: 1.0.9
10
- */
11
 
12
  /*
13
- Copyright (C) 2017 KeyCDN
14
 
15
- This program is free software; you can redistribute it and/or modify
16
- it under the terms of the GNU General Public License as published by
17
- the Free Software Foundation; either version 2 of the License, or
18
- (at your option) any later version.
19
 
20
- This program is distributed in the hope that it will be useful,
21
- but WITHOUT ANY WARRANTY; without even the implied warranty of
22
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
- GNU General Public License for more details.
24
 
25
- You should have received a copy of the GNU General Public License along
26
- with this program; if not, write to the Free Software Foundation, Inc.,
27
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28
- */
29
 
30
- /* Check & Quit */
31
- defined('ABSPATH') OR exit;
32
-
33
-
34
- /* constants */
35
- define('CDN_ENABLER_FILE', __FILE__);
36
- define('CDN_ENABLER_DIR', dirname(__FILE__));
37
- define('CDN_ENABLER_BASE', plugin_basename(__FILE__));
38
- define('CDN_ENABLER_MIN_WP', '3.8');
39
-
40
-
41
- /* loader */
42
- add_action(
43
- 'plugins_loaded',
44
- [
45
- 'CDN_Enabler',
46
- 'instance',
47
- ]
48
- );
49
-
50
-
51
- /* uninstall */
52
- register_uninstall_hook(
53
- __FILE__,
54
- [
55
- 'CDN_Enabler',
56
- 'handle_uninstall_hook',
57
- ]
58
- );
59
-
60
-
61
- /* activation */
62
- register_activation_hook(
63
- __FILE__,
64
- [
65
- 'CDN_Enabler',
66
- 'handle_activation_hook',
67
- ]
68
- );
69
-
70
-
71
- /* autoload init */
72
- spl_autoload_register('CDN_ENABLER_autoload');
73
 
74
- /* autoload funktion */
75
- function CDN_ENABLER_autoload($class) {
76
- if ( in_array($class, ['CDN_Enabler', 'CDN_Enabler_Rewriter', 'CDN_Enabler_Settings']) ) {
77
- require_once(
78
- sprintf(
79
- '%s/inc/%s.class.php',
80
- CDN_ENABLER_DIR,
81
- strtolower($class)
82
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  );
84
  }
85
  }
 
 
 
 
 
 
1
  <?php
2
  /*
3
+ Plugin Name: CDN Enabler
4
+ Text Domain: cdn-enabler
5
+ Description: Simple and fast WordPress content delivery network (CDN) integration plugin.
6
+ Author: KeyCDN
7
+ Author URI: https://www.keycdn.com
8
+ License: GPLv2 or later
9
+ Version: 2.0.0
10
+ */
11
 
12
  /*
13
+ Copyright (C) 2021 KeyCDN
14
 
15
+ This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License as published by
17
+ the Free Software Foundation; either version 2 of the License, or
18
+ (at your option) any later version.
19
 
20
+ This program is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU General Public License for more details.
24
 
25
+ You should have received a copy of the GNU General Public License along
26
+ with this program; if not, write to the Free Software Foundation, Inc.,
27
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28
+ */
29
 
30
+ if ( ! defined( 'ABSPATH' ) ) {
31
+ exit;
32
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ // constants
35
+ define( 'CDN_ENABLER_VERSION', '2.0.0' );
36
+ define( 'CDN_ENABLER_MIN_PHP', '5.6' );
37
+ define( 'CDN_ENABLER_MIN_WP', '5.1' );
38
+ define( 'CDN_ENABLER_FILE', __FILE__ );
39
+ define( 'CDN_ENABLER_BASE', plugin_basename( __FILE__ ) );
40
+ define( 'CDN_ENABLER_DIR', dirname( __FILE__ ) );
41
+
42
+ // hooks
43
+ add_action( 'plugins_loaded', array( 'CDN_Enabler', 'init' ) );
44
+ register_activation_hook( __FILE__, array( 'CDN_Enabler', 'on_activation' ) );
45
+ register_uninstall_hook( __FILE__, array( 'CDN_Enabler', 'on_uninstall' ) );
46
+
47
+ // register autoload
48
+ spl_autoload_register( 'cdn_enabler_autoload' );
49
+
50
+ // load required classes
51
+ function cdn_enabler_autoload( $class_name ) {
52
+ if ( in_array( $class_name, array( 'CDN_Enabler', 'CDN_Enabler_Engine' ) ) ) {
53
+ require_once sprintf(
54
+ '%s/inc/%s.class.php',
55
+ CDN_ENABLER_DIR,
56
+ strtolower( $class_name )
57
  );
58
  }
59
  }
60
+
61
+
62
+ // load WP-CLI command
63
+ if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'WP_CLI' ) ) {
64
+ require_once CDN_ENABLER_DIR . '/inc/cdn_enabler_cli.class.php';
65
+ }
css/settings.css ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* settings */
2
+
3
+ .subheading {
4
+ font-size: 80% !important;
5
+ font-weight: 600;
6
+ text-transform: uppercase;
7
+ }
8
+
9
+ .subheading:not(:first-of-type) {
10
+ margin-top: 15px !important;
11
+ }
12
+
13
+ .code--form-control {
14
+ line-height: 1.8;
15
+ }
css/settings.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .subheading{font-size:80%!important;font-weight:600;text-transform:uppercase}.subheading:not(:first-of-type){margin-top:15px!important}.code--form-control{line-height:1.8}
inc/cdn_enabler.class.php CHANGED
@@ -1,23 +1,25 @@
1
  <?php
2
-
3
  /**
4
- * CDN_Enabler
5
  *
6
- * @since 0.0.1
7
  */
8
 
9
- class CDN_Enabler
10
- {
 
11
 
 
12
 
13
  /**
14
- * pseudo-constructor
15
  *
16
  * @since 0.0.1
17
- * @change 0.0.1
18
  */
19
 
20
- public static function instance() {
 
21
  new self();
22
  }
23
 
@@ -26,467 +28,1075 @@ class CDN_Enabler
26
  * constructor
27
  *
28
  * @since 0.0.1
29
- * @change 1.0.9
30
  */
31
 
32
  public function __construct() {
33
- /* CDN rewriter hook */
34
- add_action(
35
- 'template_redirect',
36
- [
37
- __CLASS__,
38
- 'handle_rewrite_hook',
39
- ]
40
- );
41
 
42
- /* Rewrite rendered content in REST API */
43
- add_filter(
44
- 'the_content',
45
- [
46
- __CLASS__,
47
- 'rewrite_the_content',
48
- ],
49
- 100
50
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- /* Hooks */
53
- add_action(
54
- 'admin_init',
55
- [
56
- __CLASS__,
57
- 'register_textdomain',
58
- ]
59
- );
60
- add_action(
61
- 'admin_init',
62
- [
63
- 'CDN_Enabler_Settings',
64
- 'register_settings',
65
- ]
66
- );
67
- add_action(
68
- 'admin_menu',
69
- [
70
- 'CDN_Enabler_Settings',
71
- 'add_settings_page',
72
- ]
73
- );
74
- add_filter(
75
- 'plugin_action_links_' .CDN_ENABLER_BASE,
76
- [
77
- __CLASS__,
78
- 'add_action_link',
79
- ]
80
- );
81
 
82
- /* admin notices */
83
- add_action(
84
- 'all_admin_notices',
85
- [
86
- __CLASS__,
87
- 'cdn_enabler_requirements_check',
88
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  );
90
 
91
- /* add admin purge link */
92
- add_action(
93
- 'admin_bar_menu',
94
- [
95
- __CLASS__,
96
- 'add_admin_links',
97
- ],
98
- 90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  );
100
- /* process purge request */
101
- add_action(
102
- 'admin_notices',
103
- [
104
- __CLASS__,
105
- 'process_purge_request',
106
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  );
 
 
108
  }
109
 
110
 
111
  /**
112
- * add Zone purge link
113
  *
114
  * @since 1.0.5
115
- * @change 1.0.6
116
  *
117
- * @hook mixed
118
- *
119
- * @param object menu properties
120
  */
121
 
122
- public static function add_admin_links($wp_admin_bar) {
123
- global $wp;
124
- $options = self::get_options();
125
 
126
  // check user role
127
- if ( ! is_admin_bar_showing() or ! apply_filters('user_can_clear_cache', current_user_can('manage_options')) ) {
128
  return;
129
  }
130
 
131
- // verify Zone settings are set
132
- if ( ! is_int($options['keycdn_zone_id'])
133
- or $options['keycdn_zone_id'] <= 0 ) {
134
  return;
135
  }
136
- if ( ! array_key_exists('keycdn_api_key', $options)
137
- or strlen($options['keycdn_api_key']) < 20 ) {
 
138
  return;
139
  }
140
 
141
- // redirect to admin page if necessary so we can display notification
142
- $current_url = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' .
143
- $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
144
- $goto_url = get_admin_url();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- if ( stristr($current_url, get_admin_url()) ) {
147
- $goto_url = $current_url;
 
148
  }
 
149
 
150
- // add admin purge link
151
- $wp_admin_bar->add_menu(
152
- [
153
- 'id' => 'purge-cdn',
154
- 'href' => wp_nonce_url( add_query_arg('_cdn', 'purge', $goto_url), '_cdn__purge_nonce'),
155
- 'parent' => 'top-secondary',
156
- 'title' => '<span class="ab-item">'.esc_html__('Purge CDN', 'cdn-enabler').'</span>',
157
- 'meta' => ['title' => esc_html__('Purge CDN', 'cdn-enabler')],
158
- ]
 
 
 
 
 
 
 
159
  );
 
160
 
161
- if ( ! is_admin() ) {
162
- // add admin purge link
163
- $wp_admin_bar->add_menu(
164
- [
165
- 'id' => 'purge-cdn',
166
- 'href' => wp_nonce_url( add_query_arg('_cdn', 'purge', $goto_url), '_cdn__purge_nonce'),
167
- 'parent' => 'top-secondary',
168
- 'title' => '<span class="ab-item">'.esc_html__('Purge CDN', 'cdn-enabler').'</span>',
169
- 'meta' => ['title' => esc_html__('Purge CDN', 'cdn-enabler')],
170
- ]
171
- );
 
 
 
172
  }
 
 
 
 
 
 
173
  }
174
 
175
 
176
  /**
177
- * process purge request
178
  *
179
  * @since 1.0.5
180
- * @change 1.0.6
181
- *
182
- * @param array $data array of metadata
183
  */
184
- public static function process_purge_request($data) {
185
- $options = self::get_options();
186
 
187
- // check if clear request
188
- if ( empty($_GET['_cdn']) OR $_GET['_cdn'] !== 'purge' ) {
 
 
189
  return;
190
  }
191
 
192
  // validate nonce
193
- if ( empty($_GET['_wpnonce']) OR ! wp_verify_nonce($_GET['_wpnonce'], '_cdn__purge_nonce') ) {
194
  return;
195
  }
196
 
197
  // check user role
198
- if ( ! is_admin_bar_showing() ) {
199
  return;
200
  }
201
 
202
- // load if network
203
- if ( ! function_exists('is_plugin_active_for_network') ) {
204
- require_once( ABSPATH. 'wp-admin/includes/plugin.php' );
 
 
 
 
 
 
205
  }
206
 
207
- // API call to purge zone
208
- $response = wp_remote_get( 'https://api.keycdn.com/zones/purge/'. $options['keycdn_zone_id'] .'.json',
209
- [
210
- 'timeout' => 20,
211
- 'headers' => [
212
- 'Authorization' => 'Basic ' . base64_encode( $options['keycdn_api_key'] . ':' ),
213
- ]
214
- ]
215
- );
216
 
217
- // check results - error connecting
218
- if ( is_wp_error( $response ) ) {
219
- printf(
220
- '<div class="notice notice-error is-dismissible"><p>%s</p></div>',
221
- esc_html__('Error connecting to API - '. $response->get_error_message(), 'cdn-enabler')
222
- );
223
 
 
 
 
 
 
 
 
 
 
 
 
224
  return;
225
  }
226
 
227
- // check HTTP response
228
- if ( is_array( $response ) and is_admin_bar_showing()) {
229
- $json = json_decode($response['body'], true);
230
- $rc = wp_remote_retrieve_response_code( $response );
231
 
232
- // success
233
- if ( $rc == 200
234
- and is_array($json)
235
- and array_key_exists('description', $json) )
236
- {
237
  printf(
238
- '<div class="notice notice-success is-dismissible"><p>%s</p></div>',
239
- esc_html__($json['description'], 'cdn-enabler')
 
240
  );
241
-
242
- return;
243
- } elseif ( $rc == 200 ) {
244
- // return code 200 but no message
245
  printf(
246
- '<div class="notice notice-warning is-dismissible"><p>%s</p></div>',
247
- esc_html__('HTTP returned 200 but no message received.')
248
  );
249
-
250
- return;
251
  }
252
 
253
- // For some API errors we return custom error messages
254
- $custom_messages = array(
255
- 401 => "invalid API key",
256
- 403 => "invalid zone id",
257
- 451 => "too many failed attempts",
258
- );
259
 
260
- if ( array_key_exists($rc, $custom_messages) ) {
261
- printf(
262
- '<div class="notice notice-error is-dismissible"><p>%s</p></div>',
263
- esc_html__('HTTP returned '. $rc .': '.$custom_messages[$rc], 'cdn-enabler')
264
- );
265
 
266
- return;
267
- }
 
 
 
 
268
 
269
- // API call returned != 200 and also a status message
270
- if ( is_array($json)
271
- and array_key_exists('status', $json)
272
- and $json['status'] != "" ) {
273
- printf(
274
- '<div class="notice notice-error is-dismissible"><p>%s</p></div>',
275
- esc_html__('HTTP returned '. $rc .': '.$json['description'], 'cdn-enabler')
276
- );
277
- } else {
278
- // Something else went wrong - show HTTP error code
279
- printf(
280
- '<div class="notice notice-error is-dismissible"><p>%s</p></div>',
281
- esc_html__('HTTP returned '. $rc)
282
- );
283
- }
284
  }
285
 
 
286
 
287
- if ( ! is_admin() ) {
288
- wp_safe_redirect(
289
- remove_query_arg(
290
- '_cache',
291
- wp_get_referer()
292
- )
293
  );
294
 
295
- exit();
296
  }
297
  }
298
 
299
 
300
-
301
  /**
302
- * add action links
303
  *
304
- * @since 0.0.1
305
- * @change 0.0.1
306
  *
307
- * @param array $data alreay existing links
308
- * @return array $data extended array with links
309
  */
310
 
311
- public static function add_action_link($data) {
312
- // check permission
313
- if ( ! current_user_can('manage_options') ) {
314
- return $data;
315
- }
316
 
317
- return array_merge(
318
- $data,
319
- [
320
- sprintf(
321
- '<a href="%s">%s</a>',
322
- add_query_arg(
323
- [
324
- 'page' => 'cdn_enabler',
325
- ],
326
- admin_url('options-general.php')
327
- ),
328
- __("Settings")
329
- ),
330
- ]
331
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  }
333
 
334
 
335
  /**
336
- * run uninstall hook
337
  *
338
  * @since 0.0.1
339
- * @change 0.0.1
340
  */
341
 
342
- public static function handle_uninstall_hook() {
343
- delete_option('cdn_enabler');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
345
 
346
 
347
  /**
348
- * run activation hook
349
  *
350
- * @since 0.0.1
351
- * @change 1.0.5
352
- */
353
-
354
- public static function handle_activation_hook() {
355
- add_option(
356
- 'cdn_enabler',
357
- [
358
- 'url' => get_option('home'),
359
- 'dirs' => 'wp-content,wp-includes',
360
- 'excludes' => '.php',
361
- 'relative' => '1',
362
- 'https' => '',
363
- 'keycdn_api_key' => '',
364
- 'keycdn_zone_id' => '',
365
- ]
366
- );
367
  }
368
 
369
 
370
  /**
371
- * check plugin requirements
372
  *
373
  * @since 0.0.1
374
  * @change 0.0.1
375
  */
376
 
377
- public static function cdn_enabler_requirements_check() {
378
- // WordPress version check
379
- if ( version_compare($GLOBALS['wp_version'], CDN_ENABLER_MIN_WP.'alpha', '<') ) {
380
- show_message(
381
- sprintf(
382
- '<div class="error"><p>%s</p></div>',
383
- sprintf(
384
- __("CDN Enabler is optimized for WordPress %s. Please disable the plugin or upgrade your WordPress installation (recommended).", "cdn-enabler"),
385
- CDN_ENABLER_MIN_WP
386
- )
387
- )
388
- );
389
- }
390
  }
391
 
392
 
393
  /**
394
- * register textdomain
395
  *
396
- * @since 1.0.3
397
- * @change 1.0.3
 
 
 
398
  */
399
 
400
- public static function register_textdomain() {
401
- load_plugin_textdomain(
402
- 'cdn-enabler',
403
- false,
404
- 'cdn-enabler/lang'
405
- );
406
  }
407
 
408
 
409
  /**
410
- * return plugin options
411
  *
412
- * @since 0.0.1
413
- * @change 1.0.5
414
  *
415
- * @return array $diff data pairs
 
 
416
  */
417
 
418
- public static function get_options() {
419
- return wp_parse_args(
420
- get_option('cdn_enabler'),
421
- [
422
- 'url' => get_option('home'),
423
- 'dirs' => 'wp-content,wp-includes',
424
- 'excludes' => '.php',
425
- 'relative' => 1,
426
- 'https' => 0,
427
- 'keycdn_api_key' => '',
428
- 'keycdn_zone_id' => '',
429
- ]
430
- );
 
 
 
 
 
 
 
 
431
  }
432
 
433
 
434
  /**
435
- * return new rewriter
436
  *
437
- * @since 1.0.9
438
- * @change 1.0.9
439
  *
 
 
440
  */
441
 
442
- public static function get_rewriter() {
443
- $options = self::get_options();
 
 
 
 
 
 
444
 
445
- $excludes = array_map('trim', explode(',', $options['excludes']));
446
 
447
- return new CDN_Enabler_Rewriter(
448
- get_option('home'),
449
- $options['url'],
450
- $options['dirs'],
451
- $excludes,
452
- $options['relative'],
453
- $options['https'],
454
- $options['keycdn_api_key'],
455
- $options['keycdn_zone_id']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  }
458
 
459
 
460
  /**
461
- * run rewrite hook
462
  *
463
  * @since 0.0.1
464
- * @change 1.0.9
 
 
 
465
  */
466
 
467
- public static function handle_rewrite_hook() {
468
- $options = self::get_options();
469
 
470
- // check if origin equals cdn url
471
- if (get_option('home') == $options['url']) {
472
- return;
 
 
 
 
 
 
 
 
 
 
 
473
  }
474
 
475
- $rewriter = self::get_rewriter();
476
- ob_start(array(&$rewriter, 'rewrite'));
477
  }
478
 
479
 
480
  /**
481
- * rewrite html content
482
  *
483
- * @since 1.0.9
484
- * @change 1.0.9
485
  */
486
 
487
- public static function rewrite_the_content($html) {
488
- $rewriter = self::get_rewriter();
489
- return $rewriter->rewrite($html);
490
- }
491
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
1
  <?php
 
2
  /**
3
+ * CDN Enabler base
4
  *
5
+ * @since 0.0.1
6
  */
7
 
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
 
12
+ final class CDN_Enabler {
13
 
14
  /**
15
+ * initialize plugin
16
  *
17
  * @since 0.0.1
18
+ * @change 2.0.0
19
  */
20
 
21
+ public static function init() {
22
+
23
  new self();
24
  }
25
 
28
  * constructor
29
  *
30
  * @since 0.0.1
31
+ * @change 2.0.0
32
  */
33
 
34
  public function __construct() {
 
 
 
 
 
 
 
 
35
 
36
+ // engine hook
37
+ add_action( 'setup_theme', array( 'CDN_Enabler_Engine', 'start' ) );
38
+
39
+ // init hooks
40
+ add_action( 'init', array( __CLASS__, 'process_purge_cache_request' ) );
41
+ add_action( 'init', array( __CLASS__, 'register_textdomain' ) );
42
+
43
+ // multisite hook
44
+ add_action( 'wp_initialize_site', array( __CLASS__, 'install_later' ) );
45
+
46
+ // admin bar hook
47
+ add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_bar_items' ), 90 );
48
+
49
+ // admin interface hooks
50
+ if ( is_admin() ) {
51
+ // settings
52
+ add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
53
+ add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ) );
54
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'add_admin_resources' ) );
55
+ // dashboard
56
+ add_filter( 'plugin_action_links_' . CDN_ENABLER_BASE, array( __CLASS__, 'add_plugin_action_links' ) );
57
+ add_filter( 'plugin_row_meta', array( __CLASS__, 'add_plugin_row_meta' ), 10, 2 );
58
+ // notices
59
+ add_action( 'admin_notices', array( __CLASS__, 'requirements_check' ) );
60
+ add_action( 'admin_notices', array( __CLASS__, 'cache_purged_notice' ) );
61
+ add_action( 'admin_notices', array( __CLASS__, 'config_validated_notice' ) );
62
+ }
63
+ }
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ /**
67
+ * activation hook
68
+ *
69
+ * @since 0.0.1
70
+ * @change 2.0.0
71
+ *
72
+ * @param boolean $network_wide network activated
73
+ */
74
+
75
+ public static function on_activation( $network_wide ) {
76
+
77
+ // add backend requirements
78
+ self::each_site( $network_wide, 'self::update_backend' );
79
+ }
80
+
81
+
82
+ /**
83
+ * uninstall hook
84
+ *
85
+ * @since 2.0.0
86
+ * @change 2.0.0
87
+ */
88
+
89
+ public static function on_uninstall() {
90
+
91
+ // uninstall backend requirements
92
+ self::each_site( is_multisite(), 'self::uninstall_backend' );
93
+ }
94
+
95
+
96
+ /**
97
+ * install on new site in multisite network
98
+ *
99
+ * @since 2.0.0
100
+ * @change 2.0.0
101
+ *
102
+ * @param WP_Site $new_site new site instance
103
+ */
104
+
105
+ public static function install_later( $new_site ) {
106
+
107
+ // check if network activated
108
+ if ( ! is_plugin_active_for_network( CDN_ENABLER_BASE ) ) {
109
+ return;
110
+ }
111
+
112
+ // switch to new site
113
+ switch_to_blog( (int) $new_site->blog_id );
114
+
115
+ // add backend requirements
116
+ self::update_backend();
117
+
118
+ // restore current blog from before new site
119
+ restore_current_blog();
120
+ }
121
+
122
+
123
+ /**
124
+ * add or update backend requirements
125
+ *
126
+ * @since 2.0.0
127
+ * @change 2.0.0
128
+ *
129
+ * @return array $new_option_value new or current database option value
130
+ */
131
+
132
+ public static function update_backend() {
133
+
134
+ // get defined settings, fall back to empty array if not found
135
+ $old_option_value = get_option( 'cdn_enabler', array() );
136
+
137
+ // maybe convert old settings to new settings
138
+ $old_option_value = self::convert_settings( $old_option_value );
139
+
140
+ // update default system settings
141
+ $old_option_value = wp_parse_args( self::get_default_settings( 'system' ), $old_option_value );
142
+
143
+ // merge defined settings into default settings
144
+ $new_option_value = wp_parse_args( $old_option_value, self::get_default_settings() );
145
+
146
+ // validate settings
147
+ $new_option_value = self::validate_settings( $new_option_value );
148
+
149
+ // add or update database option
150
+ update_option( 'cdn_enabler', $new_option_value );
151
+
152
+ return $new_option_value;
153
+ }
154
+
155
+
156
+ /**
157
+ * uninstall backend requirements
158
+ *
159
+ * @since 2.0.0
160
+ * @change 2.0.0
161
+ */
162
+
163
+ private static function uninstall_backend() {
164
+
165
+ // delete database option
166
+ delete_option( 'cdn_enabler' );
167
+ }
168
+
169
+
170
+ /**
171
+ * enter each site
172
+ *
173
+ * @since 2.0.0
174
+ * @change 2.0.0
175
+ *
176
+ * @param boolean $network whether or not each site in network
177
+ * @param string $callback callback function
178
+ * @param array $callback_params callback function parameters
179
+ * @return array $callback_return returned value(s) from callback function
180
+ */
181
+
182
+ private static function each_site( $network, $callback, $callback_params = array() ) {
183
+
184
+ $callback_return = array();
185
+
186
+ if ( $network ) {
187
+ $blog_ids = self::get_blog_ids();
188
+ // switch to each site in network
189
+ foreach ( $blog_ids as $blog_id ) {
190
+ switch_to_blog( $blog_id );
191
+ $callback_return[] = (int) call_user_func_array( $callback, $callback_params );
192
+ restore_current_blog();
193
+ }
194
+ } else {
195
+ $callback_return[] = (int) call_user_func_array( $callback, $callback_params );
196
+ }
197
+
198
+ return $callback_return;
199
+ }
200
+
201
+
202
+ /**
203
+ * get settings from database
204
+ *
205
+ * @since 0.0.1
206
+ * @deprecated 2.0.0
207
+ */
208
+
209
+ public static function get_options() {
210
+
211
+ return self::get_settings();
212
+ }
213
+
214
+
215
+ /**
216
+ * get settings from database
217
+ *
218
+ * @since 2.0.0
219
+ * @change 2.0.0
220
+ *
221
+ * @return array $settings current settings from database
222
+ */
223
+
224
+ public static function get_settings() {
225
+
226
+ // get database option value
227
+ $settings = get_option( 'cdn_enabler' );
228
+
229
+ // if database option does not exist or settings are outdated
230
+ if ( $settings === false || ! isset( $settings['version'] ) || $settings['version'] !== CDN_ENABLER_VERSION ) {
231
+ $settings = self::update_backend();
232
+ }
233
+
234
+ return $settings;
235
+ }
236
+
237
+
238
+ /**
239
+ * get blog IDs
240
+ *
241
+ * @since 2.0.0
242
+ * @change 2.0.0
243
+ *
244
+ * @return array $blog_ids blog IDs
245
+ */
246
+
247
+ private static function get_blog_ids() {
248
+
249
+ $blog_ids = array( '1' );
250
+
251
+ if ( is_multisite() ) {
252
+ global $wpdb;
253
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
254
+ }
255
+
256
+ return $blog_ids;
257
+ }
258
+
259
+
260
+ /**
261
+ * get the cache purged transient name used for the purge notice
262
+ *
263
+ * @since 2.0.0
264
+ * @change 2.0.0
265
+ *
266
+ * @return string $transient_name transient name
267
+ */
268
+
269
+ private static function get_cache_purged_transient_name() {
270
+
271
+ $transient_name = 'cdn_enabler_cache_purged_' . get_current_user_id();
272
+
273
+ return $transient_name;
274
+ }
275
+
276
+
277
+ /**
278
+ * get the configuration validated transient name used for the validation notice
279
+ *
280
+ * @since 2.0.0
281
+ * @change 2.0.0
282
+ *
283
+ * @return string $transient_name transient name
284
+ */
285
+
286
+ private static function get_config_validated_transient_name() {
287
+
288
+ $transient_name = 'cdn_enabler_config_validated_' . get_current_user_id();
289
+
290
+ return $transient_name;
291
+ }
292
+
293
+
294
+ /**
295
+ * get default settings
296
+ *
297
+ * @since 2.0.0
298
+ * @change 2.0.0
299
+ *
300
+ * @param string $settings_type default `system` settings
301
+ * @return array $system_default_settings|$default_settings only default system settings or all default settings
302
+ */
303
+
304
+ private static function get_default_settings( $settings_type = null ) {
305
+
306
+ $system_default_settings = array( 'version' => (string) CDN_ENABLER_VERSION );
307
+
308
+ if ( $settings_type === 'system' ) {
309
+ return $system_default_settings;
310
+ }
311
+
312
+ $user_default_settings = array(
313
+ 'cdn_hostname' => '',
314
+ 'included_file_extensions' => implode( PHP_EOL, array(
315
+ '.avif',
316
+ '.css',
317
+ '.gif',
318
+ '.jpeg',
319
+ '.jpg',
320
+ '.json',
321
+ '.js',
322
+ '.mp3',
323
+ '.mp4',
324
+ '.pdf',
325
+ '.png',
326
+ '.svg',
327
+ '.webp',
328
+ ) ),
329
+ 'excluded_strings' => '',
330
+ 'keycdn_api_key' => '',
331
+ 'keycdn_zone_id' => '',
332
  );
333
 
334
+ // merge default settings
335
+ $default_settings = wp_parse_args( $user_default_settings, $system_default_settings );
336
+
337
+ return $default_settings;
338
+ }
339
+
340
+
341
+ /**
342
+ * convert settings to new structure
343
+ *
344
+ * @since 2.0.0
345
+ * @change 2.0.0
346
+ *
347
+ * @param array $settings settings
348
+ * @return array $settings converted settings if applicable, unchanged otherwise
349
+ */
350
+
351
+ private static function convert_settings( $settings ) {
352
+
353
+ // check if there are any settings to convert
354
+ if ( empty( $settings ) ) {
355
+ return $settings;
356
+ }
357
+
358
+ // reformatted settings
359
+ if ( isset( $settings['excludes'] ) && is_string( $settings['excludes'] ) ) {
360
+ $settings['excludes'] = str_replace( ',', PHP_EOL, $settings['excludes'] );
361
+ $settings['excludes'] = str_replace( '.php', '', $settings['excludes'] );
362
+ }
363
+
364
+ // renamed or removed settings
365
+ $settings_names = array(
366
+ // 2.0.0
367
+ 'url' => 'cdn_hostname',
368
+ 'dirs' => '', // deprecated
369
+ 'excludes' => 'excluded_strings',
370
+ 'relative' => '', // deprecated
371
+ 'https' => '', // deprecated
372
  );
373
+
374
+ foreach ( $settings_names as $old_name => $new_name ) {
375
+ if ( array_key_exists( $old_name, $settings ) ) {
376
+ if ( ! empty( $new_name ) ) {
377
+ $settings[ $new_name ] = $settings[ $old_name ];
378
+ }
379
+
380
+ unset( $settings[ $old_name ] );
381
+ }
382
+ }
383
+
384
+ return $settings;
385
+ }
386
+
387
+
388
+ /**
389
+ * add plugin action links in the plugins list table
390
+ *
391
+ * @since 2.0.0
392
+ * @change 2.0.0
393
+ *
394
+ * @param array $action_links action links
395
+ * @return array $action_links updated action links if applicable, unchanged otherwise
396
+ */
397
+
398
+ public static function add_plugin_action_links( $action_links ) {
399
+
400
+ // check user role
401
+ if ( ! current_user_can( 'manage_options' ) ) {
402
+ return $action_links;
403
+ }
404
+
405
+ // prepend action link
406
+ array_unshift( $action_links, sprintf(
407
+ '<a href="%s">%s</a>',
408
+ admin_url( 'options-general.php?page=cdn-enabler' ),
409
+ esc_html__( 'Settings', 'cdn-enabler' )
410
+ ) );
411
+
412
+ return $action_links;
413
+ }
414
+
415
+
416
+ /**
417
+ * add plugin metadata in the plugins list table
418
+ *
419
+ * @since 2.0.0
420
+ * @change 2.0.0
421
+ *
422
+ * @param array $plugin_meta plugin metadata, including the version, author, author URI, and plugin URI
423
+ * @param string $plugin_file path to the plugin file relative to the plugins directory
424
+ * @return array $plugin_meta updated action links if applicable, unchanged otherwise
425
+ */
426
+
427
+ public static function add_plugin_row_meta( $plugin_meta, $plugin_file ) {
428
+
429
+ // check if CDN Enabler row
430
+ if ( $plugin_file !== CDN_ENABLER_BASE ) {
431
+ return $plugin_meta;
432
+ }
433
+
434
+ // append metadata
435
+ $plugin_meta = wp_parse_args(
436
+ array(
437
+ '<a href="https://www.keycdn.com/support/wordpress-cdn-enabler-plugin" target="_blank" rel="nofollow noopener">Documentation</a>',
438
+ ),
439
+ $plugin_meta
440
  );
441
+
442
+ return $plugin_meta;
443
  }
444
 
445
 
446
  /**
447
+ * add admin bar items
448
  *
449
  * @since 1.0.5
450
+ * @change 2.0.0
451
  *
452
+ * @param object $wp_admin_bar menu properties
 
 
453
  */
454
 
455
+ public static function add_admin_bar_items( $wp_admin_bar ) {
 
 
456
 
457
  // check user role
458
+ if ( ! self::user_can_purge_cache() ) {
459
  return;
460
  }
461
 
462
+ // check if KeyCDN API key is set
463
+ if ( strlen( CDN_Enabler_Engine::$settings['keycdn_api_key'] ) < 20 ) {
 
464
  return;
465
  }
466
+
467
+ // check if KeyCDN Zone ID is set
468
+ if ( ! is_int( CDN_Enabler_Engine::$settings['keycdn_zone_id'] ) ) {
469
  return;
470
  }
471
 
472
+ // add admin purge link
473
+ if ( ! is_network_admin() ) {
474
+ $wp_admin_bar->add_menu(
475
+ array(
476
+ 'id' => 'cdn-enabler-purge-cache',
477
+ 'href' => wp_nonce_url( add_query_arg( array(
478
+ '_cache' => 'cdn',
479
+ '_action' => 'purge',
480
+ ) ), 'cdn_enabler_purge_cache_nonce' ),
481
+ 'parent' => 'top-secondary',
482
+ 'title' => '<span class="ab-item">' . esc_html__( 'Purge CDN Cache', 'cdn-enabler' ) . '</span>',
483
+ 'meta' => array( 'title' => esc_html__('Purge CDN Cache', 'cdn-enabler') ),
484
+ )
485
+ );
486
+ }
487
+ }
488
+
489
+
490
+ /**
491
+ * enqueue styles and scripts
492
+ *
493
+ * @since 2.0.0
494
+ * @change 2.0.0
495
+ */
496
+
497
+ public static function add_admin_resources( $hook ) {
498
 
499
+ // settings page
500
+ if ( $hook === 'settings_page_cdn-enabler' ) {
501
+ wp_enqueue_style( 'cdn-enabler-settings', plugins_url( 'css/settings.min.css', CDN_ENABLER_FILE ), array(), CDN_ENABLER_VERSION );
502
  }
503
+ }
504
 
505
+
506
+ /**
507
+ * add settings page
508
+ *
509
+ * @since 0.0.1
510
+ * @change 2.0.0
511
+ */
512
+
513
+ public static function add_settings_page() {
514
+
515
+ add_options_page(
516
+ 'CDN Enabler',
517
+ 'CDN Enabler',
518
+ 'manage_options',
519
+ 'cdn-enabler',
520
+ array( __CLASS__, 'settings_page' )
521
  );
522
+ }
523
 
524
+
525
+ /**
526
+ * check if user can purge cache
527
+ *
528
+ * @since 2.0.0
529
+ * @change 2.0.0
530
+ *
531
+ * @return boolean true if user can purge cache, false otherwise
532
+ */
533
+
534
+ private static function user_can_purge_cache() {
535
+
536
+ if ( apply_filters( 'cdn_enabler_user_can_purge_cache', current_user_can( 'manage_options' ) ) ) {
537
+ return true;
538
  }
539
+
540
+ if ( apply_filters_deprecated( 'user_can_clear_cache', array( current_user_can( 'manage_options' ) ), '2.0.0', 'cdn_enabler_user_can_purge_cache' ) ) {
541
+ return true;
542
+ }
543
+
544
+ return false;
545
  }
546
 
547
 
548
  /**
549
+ * process purge cache request
550
  *
551
  * @since 1.0.5
552
+ * @change 2.0.0
 
 
553
  */
 
 
554
 
555
+ public static function process_purge_cache_request() {
556
+
557
+ // check if purge cache request
558
+ if ( empty( $_GET['_cache'] ) || empty( $_GET['_action'] ) || $_GET['_cache'] !== 'cdn' || ( $_GET['_action'] !== 'purge' ) ) {
559
  return;
560
  }
561
 
562
  // validate nonce
563
+ if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'cdn_enabler_purge_cache_nonce' ) ) {
564
  return;
565
  }
566
 
567
  // check user role
568
+ if ( ! self::user_can_purge_cache() ) {
569
  return;
570
  }
571
 
572
+ // purge CDN cache
573
+ $response = self::purge_cdn_cache();
574
+
575
+ // redirect to same page
576
+ wp_safe_redirect( wp_get_referer() );
577
+
578
+ // set transient for purge notice
579
+ if ( is_admin() ) {
580
+ set_transient( self::get_cache_purged_transient_name(), $response );
581
  }
582
 
583
+ // purge cache request completed
584
+ exit;
585
+ }
 
 
 
 
 
 
586
 
 
 
 
 
 
 
587
 
588
+ /**
589
+ * admin notice after cache has been purged
590
+ *
591
+ * @since 2.0.0
592
+ * @change 2.0.0
593
+ */
594
+
595
+ public static function cache_purged_notice() {
596
+
597
+ // check user role
598
+ if ( ! self::user_can_purge_cache() ) {
599
  return;
600
  }
601
 
602
+ $response = get_transient( self::get_cache_purged_transient_name() );
 
 
 
603
 
604
+ if ( is_array( $response ) ) {
605
+ if ( ! empty( $response['subject'] ) ) {
 
 
 
606
  printf(
607
+ $response['wrapper'],
608
+ $response['subject'],
609
+ $response['message']
610
  );
611
+ } else {
 
 
 
612
  printf(
613
+ $response['wrapper'],
614
+ $response['message']
615
  );
 
 
616
  }
617
 
618
+ delete_transient( self::get_cache_purged_transient_name() );
619
+ }
620
+ }
 
 
 
621
 
 
 
 
 
 
622
 
623
+ /**
624
+ * admin notice after configuration has been validated
625
+ *
626
+ * @since 2.0.0
627
+ * @change 2.0.0
628
+ */
629
 
630
+ public static function config_validated_notice() {
631
+
632
+ // check user role
633
+ if ( ! current_user_can( 'manage_options' ) ) {
634
+ return;
 
 
 
 
 
 
 
 
 
 
635
  }
636
 
637
+ $response = get_transient( self::get_config_validated_transient_name() );
638
 
639
+ if ( is_array( $response ) ) {
640
+ printf(
641
+ $response['wrapper'],
642
+ $response['subject'],
643
+ $response['message']
 
644
  );
645
 
646
+ delete_transient( self::get_config_validated_transient_name() );
647
  }
648
  }
649
 
650
 
 
651
  /**
652
+ * purge CDN cache
653
  *
654
+ * @since 2.0.0
655
+ * @change 2.0.0
656
  *
657
+ * @return array $response API call response
 
658
  */
659
 
660
+ public static function purge_cdn_cache() {
 
 
 
 
661
 
662
+ // purge CDN cache API call
663
+ $response = wp_remote_get(
664
+ 'https://api.keycdn.com/zones/purge/' . CDN_Enabler_Engine::$settings['keycdn_zone_id'] . '.json',
665
+ array(
666
+ 'timeout' => 15,
667
+ 'httpversion' => '1.1',
668
+ 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( CDN_Enabler_Engine::$settings['keycdn_api_key'] . ':' ) ),
669
+ )
 
 
 
 
 
 
670
  );
671
+
672
+ // check if API call failed
673
+ if ( is_wp_error( $response ) ) {
674
+ $response = array(
675
+ 'wrapper' => '<div class="notice notice-error is-dismissible"><p><strong>%1$s</strong> %2$s</p></div>',
676
+ 'subject' => esc_html__( 'Purge CDN cache failed:', 'cdn-enabler' ),
677
+ 'message' => $response->get_error_message(),
678
+ );
679
+ // check API call response otherwise
680
+ } else {
681
+ $response_status_code = wp_remote_retrieve_response_code( $response );
682
+
683
+ if ( $response_status_code === 200 ) {
684
+ $response = array(
685
+ 'wrapper' => '<div class="notice notice-success is-dismissible"><p><strong>%s</strong></p></div>',
686
+ 'message' => esc_html__( 'CDN cache purged.', 'cdn-enabler' ),
687
+ );
688
+ } elseif ( $response_status_code >= 400 && $response_status_code <= 499 ) {
689
+ $error_messages = array(
690
+ 401 => esc_html__( 'Invalid API key.', 'cdn-enabler' ),
691
+ 403 => esc_html__( 'Invalid Zone ID.', 'cdn-enabler' ),
692
+ 429 => esc_html__( 'API rate limit exceeded.', 'cdn-enabler' ),
693
+ 451 => esc_html__( 'Too many failed attempts.', 'cdn-enabler' ),
694
+ );
695
+
696
+ if ( array_key_exists( $response_status_code, $error_messages ) ) {
697
+ $message = $error_messages[ $response_status_code ];
698
+ } else {
699
+ $message = sprintf(
700
+ // translators: %s: HTTP status code (e.g. 200)
701
+ esc_html__( '%s status code returned.', 'cdn-enabler' ),
702
+ '<code>' . $response_status_code . '</code>'
703
+ );
704
+ }
705
+
706
+ $response = array(
707
+ 'wrapper' => '<div class="notice notice-error is-dismissible"><p><strong>%s</strong> %s</p></div>',
708
+ 'subject' => esc_html__( 'Purge CDN cache failed:', 'cdn-enabler' ),
709
+ 'message' => $message,
710
+ );
711
+ } elseif ( $response_status_code >= 500 && $response_status_code <= 599 ) {
712
+ $response = array(
713
+ 'wrapper' => '<div class="notice notice-error is-dismissible"><p><strong>%s</strong> %s</p></div>',
714
+ 'subject' => esc_html__( 'Purge CDN cache failed:', 'cdn-enabler' ),
715
+ 'message' => esc_html__( 'API temporarily unavailable.', 'cdn-enabler' ),
716
+ );
717
+ }
718
+ }
719
+
720
+ return $response;
721
  }
722
 
723
 
724
  /**
725
+ * check plugin requirements
726
  *
727
  * @since 0.0.1
728
+ * @change 2.0.0
729
  */
730
 
731
+ public static function requirements_check() {
732
+
733
+ // check user role
734
+ if ( ! current_user_can( 'manage_options' ) ) {
735
+ return;
736
+ }
737
+
738
+ // check PHP version
739
+ if ( version_compare( PHP_VERSION, CDN_ENABLER_MIN_PHP, '<' ) ) {
740
+ printf(
741
+ '<div class="notice notice-error"><p>%s</p></div>',
742
+ sprintf(
743
+ // translators: 1. CDN Enabler 2. PHP version (e.g. 5.6)
744
+ esc_html__( '%1$s requires PHP %2$s or higher to function properly. Please update PHP or disable the plugin.', 'cdn-enabler' ),
745
+ '<strong>CDN Enabler</strong>',
746
+ CDN_ENABLER_MIN_PHP
747
+ )
748
+ );
749
+ }
750
+
751
+ // check WordPress version
752
+ if ( version_compare( $GLOBALS['wp_version'], CDN_ENABLER_MIN_WP . 'alpha', '<' ) ) {
753
+ printf(
754
+ '<div class="notice notice-error"><p>%s</p></div>',
755
+ sprintf(
756
+ // translators: 1. CDN Enabler 2. WordPress version (e.g. 5.1)
757
+ esc_html__( '%1$s requires WordPress %2$s or higher to function properly. Please update WordPress or disable the plugin.', 'cdn-enabler' ),
758
+ '<strong>CDN Enabler</strong>',
759
+ CDN_ENABLER_MIN_WP
760
+ )
761
+ );
762
+ }
763
  }
764
 
765
 
766
  /**
767
+ * register textdomain
768
  *
769
+ * @since 1.0.3
770
+ * @change 1.0.3
771
+ */
772
+
773
+ public static function register_textdomain() {
774
+
775
+ // load translated strings
776
+ load_plugin_textdomain( 'cdn-enabler', false, 'cdn-enabler/lang' );
 
 
 
 
 
 
 
 
 
777
  }
778
 
779
 
780
  /**
781
+ * register settings
782
  *
783
  * @since 0.0.1
784
  * @change 0.0.1
785
  */
786
 
787
+ public static function register_settings() {
788
+
789
+ register_setting( 'cdn_enabler', 'cdn_enabler', array( __CLASS__, 'validate_settings', ) );
 
 
 
 
 
 
 
 
 
 
790
  }
791
 
792
 
793
  /**
794
+ * validate CDN hostname
795
  *
796
+ * @since 2.0.0
797
+ * @change 2.0.0
798
+ *
799
+ * @param string $cdn_hostname CDN hostname
800
+ * @return string $validated_cdn_hostname validated CDN hostname
801
  */
802
 
803
+ private static function validate_cdn_hostname( $cdn_hostname ) {
804
+
805
+ $cdn_url = esc_url_raw( $cdn_hostname, array( 'http', 'https' ) );
806
+ $validated_cdn_hostname = strtolower( (string) parse_url( $cdn_url, PHP_URL_HOST ) );
807
+
808
+ return $validated_cdn_hostname;
809
  }
810
 
811
 
812
  /**
813
+ * validate textarea
814
  *
815
+ * @since 2.0.0
816
+ * @change 2.0.0
817
  *
818
+ * @param string $textarea textarea
819
+ * @param boolean $file_extension whether or not textarea requires file extension validation
820
+ * @return string $validated_textarea validated textarea
821
  */
822
 
823
+ private static function validate_textarea( $textarea, $file_extension = false ) {
824
+
825
+ $textarea = sanitize_textarea_field( $textarea );
826
+ $lines = explode( PHP_EOL, trim( $textarea ) );
827
+ $validated_textarea = array();
828
+
829
+ foreach ( $lines as $line ) {
830
+ $line = trim( $line );
831
+
832
+ if ( $line !== '' ) {
833
+ if ( ! $file_extension ) {
834
+ $validated_textarea[] = $line;
835
+ } elseif ( preg_match( '/^\.\w{1,10}$/', $line ) ) {
836
+ $validated_textarea[] = $line;
837
+ }
838
+ }
839
+ }
840
+
841
+ $validated_textarea = implode( PHP_EOL, $validated_textarea );
842
+
843
+ return $validated_textarea;
844
  }
845
 
846
 
847
  /**
848
+ * validate Zone ID
849
  *
850
+ * @since 2.0.0
851
+ * @change 2.0.0
852
  *
853
+ * @param string $zone_id Zone ID
854
+ * @return integer $validated_zone_id validated Zone ID
855
  */
856
 
857
+ private static function validate_zone_id( $zone_id ) {
858
+
859
+ $zone_id = sanitize_text_field( $zone_id );
860
+ $zone_id = abs( (int) $zone_id );
861
+ $validated_zone_id = ( $zone_id === 0 ) ? '' : $zone_id;
862
+
863
+ return $validated_zone_id;
864
+ }
865
 
 
866
 
867
+ /**
868
+ * validate configuration
869
+ *
870
+ * @since 2.0.0
871
+ * @change 2.0.0
872
+ *
873
+ * @param array $validated_settings validated settings
874
+ * @return array $validated_settings validated settings
875
+ */
876
+
877
+ private static function validate_config( $validated_settings ) {
878
+
879
+ if ( empty( $validated_settings['cdn_hostname'] ) ) {
880
+ return $validated_settings;
881
+ }
882
+
883
+ // get validation test file
884
+ $test_file = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . $validated_settings['cdn_hostname'] . '/' . str_replace( ABSPATH, '', CDN_ENABLER_DIR ) . '/css/settings.min.css';
885
+
886
+ // validation request
887
+ $response = wp_remote_get(
888
+ $test_file,
889
+ array(
890
+ 'method' => 'HEAD',
891
+ 'timeout' => 15,
892
+ 'httpversion' => '1.1',
893
+ )
894
  );
895
+
896
+ // check if validation failed
897
+ if ( is_wp_error( $response ) ) {
898
+ $response = array(
899
+ 'wrapper' => '<div class="notice notice-error is-dismissible"><p><strong>%s</strong> %s</p></div>',
900
+ 'subject' => esc_html__( 'Invalid CDN Hostname:', 'cdn-enabler' ),
901
+ 'message' => $response->get_error_message(),
902
+ );
903
+ // check validation response otherwise
904
+ } else {
905
+ $response_status_code = wp_remote_retrieve_response_code( $response );
906
+
907
+ if ( $response_status_code === 200 ) {
908
+ $response = array(
909
+ 'wrapper' => '<div class="notice notice-success is-dismissible"><p><strong>%s</strong> %s</p></div>',
910
+ 'subject' => esc_html__( 'Valid CDN Hostname:', 'cdn-enabler' ),
911
+ 'message' => sprintf(
912
+ // translators: 1. CDN Hostname (e.g. cdn.example.com) 2. HTTP status code (e.g. 200)
913
+ esc_html__( '%1$s returned a %2$s status code.', 'cdn-enabler' ),
914
+ '<code>' . $validated_settings['cdn_hostname'] . '</code>',
915
+ '<code>' . $response_status_code . '</code>'
916
+ ),
917
+ );
918
+ } else {
919
+ $response = array(
920
+ 'wrapper' => '<div class="notice notice-error is-dismissible"><p><strong>%s</strong> %s</p></div>',
921
+ 'subject' => esc_html__( 'Invalid CDN Hostname:', 'cdn-enabler' ),
922
+ 'message' => sprintf(
923
+ // translators: 1. CDN Hostname (e.g. cdn.example.com) 2. HTTP status code (e.g. 200)
924
+ esc_html__( '%1$s returned a %2$s status code.', 'cdn-enabler' ),
925
+ '<code>' . $validated_settings['cdn_hostname'] . '</code>',
926
+ '<code>' . $response_status_code . '</code>'
927
+ ),
928
+ );
929
+ }
930
+ }
931
+
932
+ // set transient for config validation notice
933
+ set_transient( self::get_config_validated_transient_name(), $response );
934
+
935
+ // validate config
936
+ if ( strpos( $response['wrapper'], 'success' ) === false ) {
937
+ $validated_settings['cdn_hostname'] = '';
938
+ }
939
+
940
+ return $validated_settings;
941
  }
942
 
943
 
944
  /**
945
+ * validate settings
946
  *
947
  * @since 0.0.1
948
+ * @change 2.0.0
949
+ *
950
+ * @param array $settings user defined settings
951
+ * @return array $validated_settings validated settings
952
  */
953
 
954
+ public static function validate_settings( $settings ) {
 
955
 
956
+ $validated_settings = array(
957
+ 'cdn_hostname' => self::validate_cdn_hostname( $settings['cdn_hostname'] ),
958
+ 'included_file_extensions' => self::validate_textarea( $settings['included_file_extensions'], true ),
959
+ 'excluded_strings' => self::validate_textarea( $settings['excluded_strings'] ),
960
+ 'keycdn_api_key' => (string) sanitize_text_field( $settings['keycdn_api_key'] ),
961
+ 'keycdn_zone_id' => self::validate_zone_id( $settings['keycdn_zone_id'] ),
962
+ );
963
+
964
+ // add default system settings
965
+ $validated_settings = wp_parse_args( $validated_settings, self::get_default_settings( 'system' ) );
966
+
967
+ // check if configuration should be validated
968
+ if ( ! empty( $settings['validate_config'] ) ) {
969
+ $validated_settings = self::validate_config( $validated_settings );
970
  }
971
 
972
+ return $validated_settings;
 
973
  }
974
 
975
 
976
  /**
977
+ * settings page
978
  *
979
+ * @since 0.0.1
980
+ * @change 2.0.0
981
  */
982
 
983
+ public static function settings_page() {
984
+
985
+ ?>
 
986
 
987
+ <div class="wrap">
988
+ <h1><?php esc_html_e( 'CDN Enabler Settings', 'cdn-enabler' ); ?></h1>
989
+
990
+ <?php
991
+ if ( strpos( CDN_Enabler_Engine::$settings['cdn_hostname'], '.kxcdn.com' ) === false && ( strlen( CDN_Enabler_Engine::$settings['keycdn_api_key'] ) < 20 || ! is_int( CDN_Enabler_Engine::$settings['keycdn_zone_id'] ) ) ) {
992
+ printf(
993
+ '<div class="notice notice-info"><p>%s</p></div>',
994
+ sprintf(
995
+ // translators: %s: KeyCDN
996
+ esc_html__( 'Combine CDN Enabler with %s for even better WordPress performance.', 'cdn-enabler' ),
997
+ '<strong><a href="https://www.keycdn.com?utm_source=wp-admin&utm_medium=plugins&utm_campaign=cdn-enabler" target="_blank" rel="nofollow noopener">KeyCDN</a></strong>'
998
+ )
999
+ );
1000
+ }
1001
+ ?>
1002
+
1003
+ <form method="post" action="options.php">
1004
+ <?php settings_fields('cdn_enabler') ?>
1005
+ <table class="form-table">
1006
+ <tr valign="top">
1007
+ <th scope="row">
1008
+ <?php esc_html_e( 'CDN Hostname', 'cdn-enabler' ); ?>
1009
+ </th>
1010
+ <td>
1011
+ <fieldset>
1012
+ <label for="cdn_enabler_cdn_hostname">
1013
+ <input name="cdn_enabler[cdn_hostname]" type="text" id="cdn_enabler_cdn_hostname" value="<?php echo esc_attr( CDN_Enabler_Engine::$settings['cdn_hostname'] ); ?>" class="regular-text code" />
1014
+ </label>
1015
+ <p class="description"><?php esc_html_e( 'URLs will be rewritten using this CDN hostname.', 'cdn-enabler' ) ?></p>
1016
+ <p>
1017
+ <?php
1018
+ // translators: 1. cdn.example.com 2. example-1a2b.kxcdn.com
1019
+ printf(
1020
+ esc_html__( 'Example: %1$s or %2$s', 'cdn-enabler' ),
1021
+ '<code class="code--form-control">cdn.example.com</code>',
1022
+ '<code class="code--form-control">example-1a2b.kxcdn.com</code>'
1023
+ );
1024
+ ?>
1025
+ </p>
1026
+ </fieldset>
1027
+ </td>
1028
+ </tr>
1029
+
1030
+ <tr valign="top">
1031
+ <th scope="row">
1032
+ <?php esc_html_e( 'CDN Inclusions', 'cdn-enabler' ); ?>
1033
+ </th>
1034
+ <td>
1035
+ <fieldset>
1036
+ <p class="subheading"><?php esc_html_e( 'File Extensions', 'cdn-enabler' ); ?></p>
1037
+ <label for="cdn_enabler_included_file_extensions">
1038
+ <textarea name="cdn_enabler[included_file_extensions]" type="text" id="cdn_enabler_included_file_extensions" class="regular-text code" rows="5" cols="40"><?php echo esc_textarea( CDN_Enabler_Engine::$settings['included_file_extensions'] ) ?></textarea>
1039
+ <p class="description"><?php esc_html_e( 'Files with these extensions will be served by the CDN. One file extension per line.', 'cdn-enabler' ); ?></p>
1040
+ </label>
1041
+ </fieldset>
1042
+ </td>
1043
+ </tr>
1044
+
1045
+ <tr valign="top">
1046
+ <th scope="row">
1047
+ <?php esc_html_e( 'CDN Exclusions', 'cdn-enabler' ); ?>
1048
+ </th>
1049
+ <td>
1050
+ <fieldset>
1051
+ <p class="subheading"><?php esc_html_e( 'Strings', 'cdn-enabler' ); ?></p>
1052
+ <label for="cdn_enabler_excluded_strings">
1053
+ <textarea name="cdn_enabler[excluded_strings]" type="text" id="cdn_enabler_excluded_strings" class="regular-text code" rows="5" cols="40"><?php echo esc_textarea( CDN_Enabler_Engine::$settings['excluded_strings'] ) ?></textarea>
1054
+ <p class="description"><?php esc_html_e( 'URLs containing these strings will not be served by the CDN. One string per line.', 'cdn-enabler' ) ?></p>
1055
+ </label>
1056
+ </fieldset>
1057
+ </td>
1058
+ </tr>
1059
+ </table>
1060
+
1061
+ <h2 class="title">Purge CDN Cache</h2>
1062
+ <p><?php esc_html_e( 'If you like, you may connect your KeyCDN account to be able to purge the CDN cache from the WordPress admin bar.', 'cdn-enabler' ) ?></p>
1063
+
1064
+ <table class="form-table">
1065
+ <tr valign="top">
1066
+ <th scope="row">
1067
+ <?php esc_html_e( 'KeyCDN API Key', 'cdn-enabler' ); ?>
1068
+ </th>
1069
+ <td>
1070
+ <fieldset>
1071
+ <label for="cdn_enabler_api_key">
1072
+ <input name="cdn_enabler[keycdn_api_key]" type="password" id="cdn_enabler_api_key" value="<?php echo esc_attr( CDN_Enabler_Engine::$settings['keycdn_api_key'] ); ?>" class="regular-text code" />
1073
+ </label>
1074
+ </fieldset>
1075
+ </td>
1076
+ </tr>
1077
+
1078
+ <tr valign="top">
1079
+ <th scope="row">
1080
+ <?php esc_html_e( 'KeyCDN Zone ID', 'cdn-enabler' ); ?>
1081
+ </th>
1082
+ <td>
1083
+ <fieldset>
1084
+ <label for="cdn_enabler_zone_id">
1085
+ <input name="cdn_enabler[keycdn_zone_id]" type="text" id="cdn_enabler_zone_id" value="<?php echo esc_attr( CDN_Enabler_Engine::$settings['keycdn_zone_id'] ); ?>" class="regular-text code" />
1086
+ </label>
1087
+ </fieldset>
1088
+ </td>
1089
+ </tr>
1090
+ </table>
1091
+
1092
+ <p class="submit">
1093
+ <input type="submit" class="button-secondary" value="<?php esc_html_e( 'Save Changes', 'cdn-enabler' ); ?>" />
1094
+ <input name="cdn_enabler[validate_config]" type="submit" class="button-primary" value="<?php esc_html_e( 'Save Changes and Validate Configuration', 'cdn-enabler' ); ?>" />
1095
+ </p>
1096
+ </form>
1097
+ </div>
1098
+
1099
+ <?php
1100
+
1101
+ }
1102
  }
inc/cdn_enabler_cli.class.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Interact with CDN Enabler.
4
+ *
5
+ * @since 2.0.0
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ class CDN_Enabler_CLI {
13
+
14
+ /**
15
+ * Purge the CDN cache.
16
+ *
17
+ * Returns an error if the purge CDN cache attempt failed.
18
+ *
19
+ * ## EXAMPLES
20
+ *
21
+ * # Purge the CDN cache.
22
+ * $ wp cdn-enabler purge
23
+ * Success: CDN cache purged.
24
+ *
25
+ * @alias purge
26
+ */
27
+
28
+ public function purge() {
29
+
30
+ $response = CDN_Enabler::purge_cdn_cache();
31
+
32
+ if ( strpos( $response['wrapper'], 'success' ) !== false ) {
33
+ return WP_CLI::success( $response['message'] );
34
+ } else {
35
+ return WP_CLI::error( $response['subject'] . ' ' . $response['message'] );
36
+ }
37
+ }
38
+ }
39
+
40
+ // add WP-CLI command
41
+ WP_CLI::add_command( 'cdn-enabler', 'CDN_Enabler_CLI' );
inc/cdn_enabler_engine.class.php ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CDN Enabler engine
4
+ *
5
+ * @since 2.0.0
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ final class CDN_Enabler_Engine {
13
+
14
+ /**
15
+ * start engine
16
+ *
17
+ * @since 2.0.0
18
+ * @change 2.0.0
19
+ */
20
+
21
+ public static function start() {
22
+
23
+ new self();
24
+ }
25
+
26
+
27
+ /**
28
+ * engine settings from database
29
+ *
30
+ * @since 2.0.0
31
+ * @change 2.0.0
32
+ *
33
+ * @var array
34
+ */
35
+
36
+ public static $settings;
37
+
38
+
39
+ /**
40
+ * constructor
41
+ *
42
+ * @since 2.0.0
43
+ * @change 2.0.0
44
+ */
45
+
46
+ public function __construct() {
47
+
48
+ // get settings from database
49
+ self::$settings = CDN_Enabler::get_settings();
50
+
51
+ if ( ! empty( self::$settings ) ) {
52
+ self::start_buffering();
53
+ }
54
+ }
55
+
56
+
57
+ /**
58
+ * start output buffering
59
+ *
60
+ * @since 2.0.0
61
+ * @change 2.0.0
62
+ */
63
+
64
+ private static function start_buffering() {
65
+
66
+ ob_start( 'self::end_buffering' );
67
+ }
68
+
69
+
70
+ /**
71
+ * end output buffering and rewrite URLs if applicable
72
+ *
73
+ * @since 2.0.0
74
+ * @change 2.0.0
75
+ *
76
+ * @param string $page_contents contents of a page from the output buffer
77
+ * @param integer $phase bitmask of PHP_OUTPUT_HANDLER_* constants
78
+ * @return string $page_contents maybe rewritten contents of a page from the output buffer
79
+ */
80
+
81
+ private static function end_buffering( $page_contents, $phase ) {
82
+
83
+ if ( $phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END ) {
84
+ if ( self::bypass_rewrite() ) {
85
+ return $page_contents;
86
+ }
87
+
88
+ $page_contents = apply_filters( 'cdn_enabler_page_contents_before_rewrite', $page_contents );
89
+
90
+ $page_contents = self::rewriter( $page_contents );
91
+
92
+ return $page_contents;
93
+ }
94
+ }
95
+
96
+
97
+ /**
98
+ * check if file URL is excluded from rewrite
99
+ *
100
+ * @since 2.0.0
101
+ * @change 2.0.0
102
+ *
103
+ * @param string $file_url full or relative URL to potentially exclude from being rewritten
104
+ * @return boolean true if file URL is excluded from the rewrite, false otherwise
105
+ */
106
+
107
+ private static function is_excluded( $file_url ) {
108
+
109
+ // if string excluded (case sensitive)
110
+ if ( ! empty( self::$settings['excluded_strings'] ) ) {
111
+ $excluded_strings = explode( PHP_EOL, self::$settings['excluded_strings'] );
112
+
113
+ foreach ( $excluded_strings as $excluded_string ) {
114
+ if ( strpos( $file_url, $excluded_string ) !== false ) {
115
+ return true;
116
+ }
117
+ }
118
+ }
119
+
120
+ return false;
121
+ }
122
+
123
+
124
+ /**
125
+ * check if rewrite should be bypassed
126
+ *
127
+ * @since 2.0.0
128
+ * @change 2.0.0
129
+ *
130
+ * @return boolean true if rewrite should be bypassed, false otherwise
131
+ */
132
+
133
+ private static function bypass_rewrite() {
134
+
135
+ // bypass rewrite hook
136
+ if ( apply_filters( 'cdn_enabler_bypass_rewrite', false ) ) {
137
+ return true;
138
+ }
139
+
140
+ // check request method
141
+ if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' ) {
142
+ return true;
143
+ }
144
+
145
+ // check conditional tags
146
+ if ( is_admin() || is_trackback() || is_robots() || is_preview() ) {
147
+ return true;
148
+ }
149
+
150
+ return false;
151
+ }
152
+
153
+
154
+ /**
155
+ * rewrite URL to use CDN hostname
156
+ *
157
+ * @since 0.0.1
158
+ * @change 2.0.0
159
+ *
160
+ * @param array $matches pattern matches from parsed file contents
161
+ * @return string $file_url rewritten file URL if applicable, unchanged otherwise
162
+ */
163
+
164
+ private static function rewrite_url( $matches ) {
165
+
166
+ $file_url = $matches[0];
167
+ $site_hostname = ( ! empty( $_SERVER['HTTP_HOST'] ) ) ? $_SERVER['HTTP_HOST'] : parse_url( home_url(), PHP_URL_HOST );
168
+ $site_hostnames = (array) apply_filters( 'cdn_enabler_site_hostnames', array( $site_hostname ) );
169
+ $cdn_hostname = self::$settings['cdn_hostname'];
170
+
171
+ // if excluded or already using CDN hostname
172
+ if ( self::is_excluded( $file_url ) || stripos( $file_url, $cdn_hostname ) !== false ) {
173
+ return $file_url;
174
+ }
175
+
176
+ // rewrite full URL (e.g. https://www.example.com/wp..., https:\/\/www.example.com\/wp..., or //www.example.com/wp...)
177
+ foreach ( $site_hostnames as $site_hostname ) {
178
+ if ( stripos( $file_url, '/' . $site_hostname ) !== false ) {
179
+ return str_replace( $site_hostname, $cdn_hostname, $file_url );
180
+ }
181
+ }
182
+
183
+ // rewrite relative URLs hook
184
+ if ( apply_filters( 'cdn_enabler_rewrite_relative_urls', true ) ) {
185
+ // rewrite relative URL (e.g. /wp-content/uploads/example.jpg)
186
+ if ( strpos( $file_url, '//' ) !== 0 && strpos( $file_url, '/' ) === 0 ) {
187
+ return '//' . $cdn_hostname . $file_url;
188
+ }
189
+
190
+ // rewrite escaped relative URL (e.g. \/wp-content\/uploads\/example.jpg)
191
+ if ( strpos( $file_url, '\/\/' ) !== 0 && strpos( $file_url, '\/' ) === 0 ) {
192
+ return '\/\/' . $cdn_hostname . $file_url;
193
+ }
194
+ }
195
+
196
+ return $file_url;
197
+ }
198
+
199
+
200
+ /**
201
+ * rewrite file contents
202
+ *
203
+ * @since 0.0.1
204
+ * @change 2.0.0
205
+ *
206
+ * @param string $contents contents of file
207
+ * @return string $contents|$rewritten_contents rewritten file contents if applicable, unchanged otherwise
208
+ */
209
+
210
+ public static function rewriter( $contents ) {
211
+
212
+ // check rewrite requirements
213
+ if ( ! is_string( $contents ) || empty( self::$settings['cdn_hostname'] ) || empty( self::$settings['included_file_extensions'] ) ) {
214
+ return $contents;
215
+ }
216
+
217
+ $included_file_extensions_regex = quotemeta( implode( '|', explode( PHP_EOL, self::$settings['included_file_extensions'] ) ) );
218
+
219
+ $urls_regex = '#[^\"\'\s=>(,]+(' . $included_file_extensions_regex . ')(?=\/?[?\\\"\'\s)>])#i';
220
+
221
+ $rewritten_contents = preg_replace_callback( $urls_regex, 'self::rewrite_url', $contents );
222
+
223
+ return $rewritten_contents;
224
+ }
225
+ }
inc/cdn_enabler_rewriter.class.php DELETED
@@ -1,194 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * CDN_Enabler_Rewriter
5
- *
6
- * @since 0.0.1
7
- */
8
-
9
- class CDN_Enabler_Rewriter
10
- {
11
- var $blog_url = null; // origin URL
12
- var $cdn_url = null; // CDN URL
13
-
14
- var $dirs = null; // included directories
15
- var $excludes = []; // excludes
16
- var $relative = false; // use CDN on relative paths
17
- var $https = false; // use CDN on HTTPS
18
- var $keycdn_api_key = null; // optional API key for KeyCDN
19
- var $keycdn_zone_id = null; // optional KeyCDN Zone ID
20
-
21
- /**
22
- * constructor
23
- *
24
- * @since 0.0.1
25
- * @change 1.0.5
26
- */
27
-
28
- function __construct(
29
- $blog_url,
30
- $cdn_url,
31
- $dirs,
32
- array $excludes,
33
- $relative,
34
- $https,
35
- $keycdn_api_key,
36
- $keycdn_zone_id
37
- ) {
38
- $this->blog_url = $blog_url;
39
- $this->cdn_url = $cdn_url;
40
- $this->dirs = $dirs;
41
- $this->excludes = $excludes;
42
- $this->relative = $relative;
43
- $this->https = $https;
44
- $this->keycdn_api_key = $keycdn_api_key;
45
- $this->keycdn_zone_id = $keycdn_zone_id;
46
- }
47
-
48
-
49
- /**
50
- * exclude assets that should not be rewritten
51
- *
52
- * @since 0.0.1
53
- * @change 1.0.3
54
- *
55
- * @param string $asset current asset
56
- * @return boolean true if need to be excluded
57
- */
58
-
59
- protected function exclude_asset(&$asset) {
60
- // excludes
61
- foreach ($this->excludes as $exclude) {
62
- if (!!$exclude && stristr($asset, $exclude) != false) {
63
- return true;
64
- }
65
- }
66
- return false;
67
- }
68
-
69
-
70
- /**
71
- * relative url
72
- *
73
- * @since 1.0.5
74
- * @change 1.0.5
75
- *
76
- * @param string $url a full url
77
- * @return string protocol relative url
78
- */
79
- protected function relative_url($url) {
80
- return substr($url, strpos($url, '//'));
81
- }
82
-
83
-
84
- /**
85
- * rewrite url
86
- *
87
- * @since 0.0.1
88
- * @change 1.0.7
89
- *
90
- * @param string $asset current asset
91
- * @return string updated url if not excluded
92
- */
93
-
94
- protected function rewrite_url(&$asset) {
95
- if ($this->exclude_asset($asset[0])) {
96
- return $asset[0];
97
- }
98
-
99
- // Don't rewrite if in preview mode
100
- if ( is_admin_bar_showing()
101
- and array_key_exists('preview', $_GET)
102
- and $_GET['preview'] == 'true' )
103
- {
104
- return $asset[0];
105
- }
106
-
107
- $blog_url = $this->relative_url($this->blog_url);
108
- $subst_urls = [ 'http:'.$blog_url ];
109
-
110
- // rewrite both http and https URLs if we ticked 'enable CDN for HTTPS connections'
111
- if ($this->https) {
112
- $subst_urls = [
113
- 'http:'.$blog_url,
114
- 'https:'.$blog_url,
115
- ];
116
- }
117
-
118
- // is it a protocol independent URL?
119
- if (strpos($asset[0], '//') === 0) {
120
- return str_replace($blog_url, $this->cdn_url, $asset[0]);
121
- }
122
-
123
- // check if not a relative path
124
- if (!$this->relative || strstr($asset[0], $blog_url)) {
125
- return str_replace($subst_urls, $this->cdn_url, $asset[0]);
126
- }
127
-
128
- // relative URL
129
- return $this->cdn_url . $asset[0];
130
- }
131
-
132
-
133
- /**
134
- * get directory scope
135
- *
136
- * @since 0.0.1
137
- * @change 0.0.1
138
- *
139
- * @return string directory scope
140
- */
141
-
142
- protected function get_dir_scope() {
143
- $input = explode(',', $this->dirs);
144
-
145
- // default
146
- if ($this->dirs == '' || count($input) < 1) {
147
- return 'wp\-content|wp\-includes';
148
- }
149
-
150
- return implode('|', array_map('quotemeta', array_map('trim', $input)));
151
- }
152
-
153
-
154
- /**
155
- * rewrite url
156
- *
157
- * @since 0.0.1
158
- * @change 1.0.7
159
- *
160
- * @param string $html current raw HTML doc
161
- * @return string updated HTML doc with CDN links
162
- */
163
-
164
- public function rewrite($html) {
165
- // check if HTTPS and use CDN over HTTPS enabled
166
- if (!$this->https && isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') {
167
- return $html;
168
- }
169
-
170
- // get dir scope in regex format
171
- $dirs = $this->get_dir_scope();
172
- $blog_url = $this->https
173
- ? '(https?:|)'.$this->relative_url(quotemeta($this->blog_url))
174
- : '(http:|)'.$this->relative_url(quotemeta($this->blog_url));
175
-
176
- // regex rule start
177
- $regex_rule = '#(?<=[(\"\'])';
178
-
179
- // check if relative paths
180
- if ($this->relative) {
181
- $regex_rule .= '(?:'.$blog_url.')?';
182
- } else {
183
- $regex_rule .= $blog_url;
184
- }
185
-
186
- // regex rule end
187
- $regex_rule .= '/(?:((?:'.$dirs.')[^\"\')]+)|([^/\"\']+\.[^/\"\')]+))(?=[\"\')])#';
188
-
189
- // call the cdn rewriter callback
190
- $cdn_html = preg_replace_callback($regex_rule, [$this, 'rewrite_url'], $html);
191
-
192
- return $cdn_html;
193
- }
194
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/cdn_enabler_settings.class.php DELETED
@@ -1,249 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * CDN_Enabler_Settings
5
- *
6
- * @since 0.0.1
7
- */
8
-
9
- class CDN_Enabler_Settings
10
- {
11
-
12
-
13
- /**
14
- * register settings
15
- *
16
- * @since 0.0.1
17
- * @change 0.0.1
18
- */
19
-
20
- public static function register_settings()
21
- {
22
- register_setting(
23
- 'cdn_enabler',
24
- 'cdn_enabler',
25
- [
26
- __CLASS__,
27
- 'validate_settings',
28
- ]
29
- );
30
- }
31
-
32
-
33
- /**
34
- * validation of settings
35
- *
36
- * @since 0.0.1
37
- * @change 1.0.5
38
- *
39
- * @param array $data array with form data
40
- * @return array array with validated values
41
- */
42
-
43
- public static function validate_settings($data)
44
- {
45
- if (!isset($data['relative'])) {
46
- $data['relative'] = 0;
47
- }
48
- if (!isset($data['https'])) {
49
- $data['https'] = 0;
50
- }
51
- if (!isset($data['keycdn_api_key'])) {
52
- $data['keycdn_api_key'] = "";
53
- }
54
- if (!isset($data['keycdn_zone_id'])) {
55
- $data['keycdn_zone_id'] = "";
56
- }
57
-
58
- return [
59
- 'url' => esc_url($data['url']),
60
- 'dirs' => esc_attr($data['dirs']),
61
- 'excludes' => esc_attr($data['excludes']),
62
- 'relative' => (int)($data['relative']),
63
- 'https' => (int)($data['https']),
64
- 'keycdn_api_key' => esc_attr($data['keycdn_api_key']),
65
- 'keycdn_zone_id' => (int)($data['keycdn_zone_id']),
66
- ];
67
- }
68
-
69
-
70
- /**
71
- * add settings page
72
- *
73
- * @since 0.0.1
74
- * @change 0.0.1
75
- */
76
-
77
- public static function add_settings_page()
78
- {
79
- $page = add_options_page(
80
- 'CDN Enabler',
81
- 'CDN Enabler',
82
- 'manage_options',
83
- 'cdn_enabler',
84
- [
85
- __CLASS__,
86
- 'settings_page',
87
- ]
88
- );
89
- }
90
-
91
-
92
- /**
93
- * settings page
94
- *
95
- * @since 0.0.1
96
- * @change 1.0.6
97
- *
98
- * @return void
99
- */
100
-
101
- public static function settings_page()
102
- {
103
- $options = CDN_Enabler::get_options()
104
-
105
-
106
- ?>
107
- <div class="wrap">
108
- <h2>
109
- <?php _e("CDN Enabler Settings", "cdn-enabler"); ?>
110
- </h2>
111
-
112
- <?php
113
- if (( ! is_int($options['keycdn_zone_id'])
114
- or $options['keycdn_zone_id'] <= 0 )
115
- or
116
- ( ! array_key_exists('keycdn_api_key', $options)
117
- or strlen($options['keycdn_api_key']) < 20 ))
118
- {
119
- printf(__('
120
- <div class="notice notice-info">
121
- <p>Combine CDN Enabler with <b><a href="%s">%s</a></b> for even faster WordPress performance.</p>
122
- </div>'), 'https://www.keycdn.com?utm_source=wp-admin&utm_medium=plugins&utm_campaign=cdn-enabler', 'KeyCDN');
123
- }
124
- ?>
125
-
126
- <form method="post" action="options.php">
127
- <?php settings_fields('cdn_enabler') ?>
128
-
129
- <table class="form-table">
130
-
131
- <tr valign="top">
132
- <th scope="row">
133
- <?php _e("CDN URL", "cdn-enabler"); ?>
134
- </th>
135
- <td>
136
- <fieldset>
137
- <label for="cdn_enabler_url">
138
- <input type="text" name="cdn_enabler[url]" id="cdn_enabler_url" value="<?php echo $options['url']; ?>" size="64" class="regular-text code" />
139
- </label>
140
-
141
- <p class="description">
142
- <?php _e("Enter the CDN URL without trailing", "cdn-enabler"); ?> <code>/</code>
143
- </p>
144
- </fieldset>
145
- </td>
146
- </tr>
147
-
148
- <tr valign="top">
149
- <th scope="row">
150
- <?php _e("Included Directories", "cdn-enabler"); ?>
151
- </th>
152
- <td>
153
- <fieldset>
154
- <label for="cdn_enabler_dirs">
155
- <input type="text" name="cdn_enabler[dirs]" id="cdn_enabler_dirs" value="<?php echo $options['dirs']; ?>" size="64" class="regular-text code" />
156
- <?php _e("Default: <code>wp-content,wp-includes</code>", "cdn-enabler"); ?>
157
- </label>
158
-
159
- <p class="description">
160
- <?php _e("Assets in these directories will be pointed to the CDN URL. Enter the directories separated by", "cdn-enabler"); ?> <code>,</code>
161
- </p>
162
- </fieldset>
163
- </td>
164
- </tr>
165
-
166
- <tr valign="top">
167
- <th scope="row">
168
- <?php _e("Exclusions", "cdn-enabler"); ?>
169
- </th>
170
- <td>
171
- <fieldset>
172
- <label for="cdn_enabler_excludes">
173
- <input type="text" name="cdn_enabler[excludes]" id="cdn_enabler_excludes" value="<?php echo $options['excludes']; ?>" size="64" class="regular-text code" />
174
- <?php _e("Default: <code>.php</code>", "cdn-enabler"); ?>
175
- </label>
176
-
177
- <p class="description">
178
- <?php _e("Enter the exclusions (directories or extensions) separated by", "cdn-enabler"); ?> <code>,</code>
179
- </p>
180
- </fieldset>
181
- </td>
182
- </tr>
183
-
184
- <tr valign="top">
185
- <th scope="row">
186
- <?php _e("Relative Path", "cdn-enabler"); ?>
187
- </th>
188
- <td>
189
- <fieldset>
190
- <label for="cdn_enabler_relative">
191
- <input type="checkbox" name="cdn_enabler[relative]" id="cdn_enabler_relative" value="1" <?php checked(1, $options['relative']) ?> />
192
- <?php _e("Enable CDN for relative paths (default: enabled).", "cdn-enabler"); ?>
193
- </label>
194
- </fieldset>
195
- </td>
196
- </tr>
197
-
198
- <tr valign="top">
199
- <th scope="row">
200
- <?php _e("CDN HTTPS", "cdn-enabler"); ?>
201
- </th>
202
- <td>
203
- <fieldset>
204
- <label for="cdn_enabler_https">
205
- <input type="checkbox" name="cdn_enabler[https]" id="cdn_enabler_https" value="1" <?php checked(1, $options['https']) ?> />
206
- <?php _e("Enable CDN for HTTPS connections (default: disabled).", "cdn-enabler"); ?>
207
- </label>
208
- </fieldset>
209
- </td>
210
- </tr>
211
-
212
- <tr valign="top">
213
- <th scope="row">
214
- <?php _e("KeyCDN API Key", "cdn-enabler"); ?>
215
- </th>
216
- <td>
217
- <fieldset>
218
- <label for="cdn_enabler_api_key">
219
- <input type="password" name="cdn_enabler[keycdn_api_key]" id="cdn_enabler_api_key" value="<?php echo $options['keycdn_api_key']; ?>" size="64" class="regular-text code" />
220
- <p class="description">
221
- <?php _e("KeyCDN API key to purge zone on request", "cdn-enabler"); ?>
222
- </p>
223
- </label>
224
- </fieldset>
225
- </td>
226
- </tr>
227
-
228
- <tr valign="top">
229
- <th scope="row">
230
- <?php _e("KeyCDN Zone ID", "cdn-enabler"); ?>
231
- </th>
232
- <td>
233
- <fieldset>
234
- <label for="cdn_enabler_zone_id">
235
- <input type="text" name="cdn_enabler[keycdn_zone_id]" id="cdn_enabler_zone_id" value="<?php echo $options['keycdn_zone_id']; ?>" size="64" class="regular-text code" />
236
- <p class="description">
237
- <?php _e("KeyCDN Zone ID of the zone to purge on request", "cdn-enabler"); ?>
238
- </p>
239
- </label>
240
- </fieldset>
241
- </td>
242
- </tr>
243
- </table>
244
-
245
- <?php submit_button() ?>
246
- </form>
247
- </div><?php
248
- }
249
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,52 +1,65 @@
1
- === CDN Enabler - WordPress CDN Plugin ===
2
  Contributors: keycdn
3
  Tags: cdn, content delivery network, content distribution network
4
- Requires at least: 4.6
5
- Tested up to: 5.1
 
6
  Stable tag: trunk
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
 
11
-
12
- Enable CDN URLs for your static assets such as images, CSS or JavaScript files.
13
-
14
 
15
 
16
  == Description ==
 
17
 
18
- A **content delivery network (CDN)** is a network of distributed edge servers, which accelerate your content around the globe. The main benefits of a CDN are *scalability*, *reliability* and *performance*. The **CDN Enabler** plugin helps you to quickly and easily integrate a CDN in WordPress.
19
-
20
- = What it does? =
21
- The CDN Enabler plugin has been developed to link your content to the CDN URLs.
22
 
23
  = Features =
24
- * Link assets to load from a CDN
25
- * Set included directories
26
- * Define exclusions (directories or extensions)
27
- * Enable or disable for HTTPS
28
- * Supports [Bedrock](https://roots.io/bedrock/ "Bedrock CDN")
 
29
 
30
- > The CDN Enabler works perfectly with the fast and lightweight [WordPress Cache Enabler](https://wordpress.org/plugins/cache-enabler/) plugin.
31
 
 
 
32
 
33
- = System Requirements =
34
- * PHP >=5.6
35
- * WordPress >=4.6
36
 
 
 
 
 
 
 
37
 
38
- = Contribute =
39
- * Anyone is welcome to contribute to the plugin on [GitHub](https://github.com/keycdn/cdn-enabler).
40
- * Please merge (squash) all your changes into a single commit before you open a pull request.
41
 
 
 
42
 
43
- = Author =
44
- * [KeyCDN](https://www.keycdn.com "KeyCDN")
45
 
 
 
46
 
47
 
48
  == Changelog ==
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  = 1.0.9 =
51
  * Rewrite URLs filtering the_content so that rendered HTML in REST API use CDN
52
 
1
+ === CDN Enabler ===
2
  Contributors: keycdn
3
  Tags: cdn, content delivery network, content distribution network
4
+ Requires at least: 5.1
5
+ Tested up to: 5.6
6
+ Requires PHP: 5.6
7
  Stable tag: trunk
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
 
12
+ A content delivery network (CDN) integration plugin for WordPress that rewrites URLs, like for CSS, JavaScript, and images, to be served by a CDN.
 
 
13
 
14
 
15
  == Description ==
16
+ CDN Enabler is a simple and easy to use WordPress plugin that rewrites URLs, such as those for CSS, JavaScript, and images, to be served by a content delivery network (CDN). This helps improve site performance, reliability, and scalability by offloading the majority of traffic to a CDN.
17
 
 
 
 
 
18
 
19
  = Features =
20
+ * Fast and efficient rewrite engine
21
+ * Manual and WP-CLI cache purging (when a [KeyCDN](https://www.keycdn.com) account is connected)
22
+ * Include URLs in the rewrite by file extensions
23
+ * Exclude URLs in the rewrite by strings
24
+ * WordPress multisite network support
25
+ * Works perfectly with [Cache Enabler](https://wordpress.org/plugins/cache-enabler/) and the majority of third party plugins
26
 
 
27
 
28
+ = How does the rewriting work? =
29
+ CDN Enabler captures page contents and rewrites URLs to be served by the designated CDN.
30
 
 
 
 
31
 
32
+ = Documentation =
33
+ * [Installation](https://www.keycdn.com/support/wordpress-cdn-enabler-plugin#installation)
34
+ * [Settings](https://www.keycdn.com/support/wordpress-cdn-enabler-plugin#settings)
35
+ * [Hooks](https://www.keycdn.com/support/wordpress-cdn-enabler-plugin#hooks)
36
+ * [WP-CLI](https://www.keycdn.com/support/wordpress-cdn-enabler-plugin#wp-cli)
37
+ * [FAQ](https://www.keycdn.com/support/wordpress-cdn-enabler-plugin#faq)
38
 
 
 
 
39
 
40
+ = Want to help? =
41
+ * Want to file a bug, contribute some code, or improve translations? Excellent! Check out our [GitHub issues](https://github.com/keycdn/cdn-enabler/issues) or [translations](https://translate.wordpress.org/projects/wp-plugins/cdn-enabler/).
42
 
 
 
43
 
44
+ = Maintainer =
45
+ * [KeyCDN](https://www.keycdn.com)
46
 
47
 
48
  == Changelog ==
49
 
50
+ = 2.0.0 =
51
+ * Update output buffer timing to start earlier on the `setup_theme` hook instead of the `template_redirect` hook (#23)
52
+ * Update settings (#23)
53
+ * Update requirements check (#23)
54
+ * Update purge CDN cache handling (#23)
55
+ * Add new rewrite engine (#23)
56
+ * Add WP-CLI cache purging (#23)
57
+ * Add configuration validation (#23)
58
+ * Add `cdn_enabler_user_can_purge_cache`, `cdn_enabler_page_contents_before_rewrite`, `cdn_enabler_bypass_rewrite`, `cdn_enabler_site_hostnames`, and `cdn_enabler_rewrite_relative_urls` filter hooks (#23)
59
+ * Fix requirement notices being shown to all users (#23)
60
+ * Fix rewriting limitations (#23)
61
+ * Deprecate `user_can_clear_cache` filter hook in favor of replacement (#23)
62
+
63
  = 1.0.9 =
64
  * Rewrite URLs filtering the_content so that rendered HTML in REST API use CDN
65