W3 Total Cache - Version 2.2.6

Version Description

  • Fix: Error clearing all cache when using Cloudfront full CDN in Pro
Download this release

Release Info

Developer joemoto
Plugin Icon 128x128 W3 Total Cache
Version 2.2.6
Comparing to
See all releases

Code changes from version 2.2.4 to 2.2.6

Base_Page_Settings.php CHANGED
@@ -1,6 +1,17 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
 
 
 
 
 
4
  class Base_Page_Settings {
5
  /**
6
  * Config
@@ -44,24 +55,34 @@ class Base_Page_Settings {
44
  */
45
  protected $_page;
46
 
47
- function __construct() {
 
 
 
48
  $this->_config = Dispatcher::config();
49
  $this->_config_master = Dispatcher::config_master();
50
  $this->_page = Util_Admin::get_current_page();
51
  }
52
 
53
- function options() {
 
 
 
54
  $this->view();
55
  }
56
 
 
 
 
57
  public function render_footer() {
58
  include W3TC_INC_OPTIONS_DIR . '/common/footer.php';
59
  }
60
 
61
  /**
62
- * Returns true if config section is sealed
 
 
63
  *
64
- * @param string $section
65
  * @return boolean
66
  */
67
  protected function is_sealed( $section ) {
@@ -69,7 +90,7 @@ class Base_Page_Settings {
69
  }
70
 
71
  /**
72
- * Returns true if we edit master config
73
  *
74
  * @return boolean
75
  */
@@ -78,12 +99,13 @@ class Base_Page_Settings {
78
  }
79
 
80
  /**
81
- * Prints checkbox with config option value
82
  *
83
- * @param string $option_id
84
- * @param bool $disabled
85
- * @param string $class_prefix
86
- * @param bool $label
 
87
  */
88
  protected function checkbox( $option_id, $disabled = false, $class_prefix = '', $label = true, $force_value = null ) {
89
  $disabled = $disabled || $this->_config->is_sealed( $option_id );
@@ -101,6 +123,14 @@ class Base_Page_Settings {
101
 
102
  if ( ! is_null( $force_value ) ) {
103
  checked( $force_value, true );
 
 
 
 
 
 
 
 
104
  } else {
105
  checked( $this->_config->get_boolean( $option_id ), true );
106
  }
@@ -115,10 +145,10 @@ class Base_Page_Settings {
115
  /**
116
  * Prints a radio button and if config value matches value
117
  *
118
- * @param string $option_id config id.
119
- * @param unknown $value
120
- * @param bool $disabled
121
- * @param string $class_prefix
122
  */
123
  protected function radio( $option_id, $value, $disabled = false, $class_prefix = '' ) {
124
  if ( is_bool( $value ) ) {
@@ -143,9 +173,9 @@ class Base_Page_Settings {
143
  }
144
 
145
  /**
146
- * Prints checkbox for debug option
147
  *
148
- * @param string $option_id
149
  */
150
  protected function checkbox_debug( $option_id ) {
151
  if ( is_array( $option_id ) ) {
@@ -175,6 +205,13 @@ class Base_Page_Settings {
175
  echo ' />';
176
  }
177
 
 
 
 
 
 
 
 
178
  protected function checkbox_debug_pro( $option_id, $label, $label_pro ) {
179
  if ( is_array( $option_id ) ) {
180
  $section = $option_id[0];
@@ -220,6 +257,13 @@ class Base_Page_Settings {
220
  echo '</label>';
221
  }
222
 
 
 
 
 
 
 
 
223
  protected function value_with_disabled( $option_id, $disabled, $value_when_disabled ) {
224
  if ( $disabled ) {
225
  echo 'value="' . esc_attr( $value_when_disabled ) . '" disabled="disabled" ';
@@ -228,6 +272,9 @@ class Base_Page_Settings {
228
  }
229
  }
230
 
 
 
 
231
  protected function view() {
232
  include W3TC_INC_DIR . '/options/common/header.php';
233
  }
1
  <?php
2
+ /**
3
+ * File: Base_Page_Settings.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
+ /**
11
+ * Class: Base_Page_Settings
12
+ *
13
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
14
+ */
15
  class Base_Page_Settings {
16
  /**
17
  * Config
55
  */
56
  protected $_page;
57
 
58
+ /**
59
+ * Constructor.
60
+ */
61
+ public function __construct() {
62
  $this->_config = Dispatcher::config();
63
  $this->_config_master = Dispatcher::config_master();
64
  $this->_page = Util_Admin::get_current_page();
65
  }
66
 
67
+ /**
68
+ * Render header.
69
+ */
70
+ public function options() {
71
  $this->view();
72
  }
73
 
74
+ /**
75
+ * Render footer.
76
+ */
77
  public function render_footer() {
78
  include W3TC_INC_OPTIONS_DIR . '/common/footer.php';
79
  }
80
 
81
  /**
82
+ * Returns true if config section is sealed.
83
+ *
84
+ * @param string $section Config section.
85
  *
 
86
  * @return boolean
87
  */
88
  protected function is_sealed( $section ) {
90
  }
91
 
92
  /**
93
+ * Returns true if we edit master config.
94
  *
95
  * @return boolean
96
  */
99
  }
100
 
101
  /**
102
+ * Prints checkbox with config option value.
103
  *
104
+ * @param string $option_id Option ID.
105
+ * @param bool $disabled Disabled flag.
106
+ * @param string $class_prefix Class prefix.
107
+ * @param bool $label Label.
108
+ * @param bool $force_value Override value.
109
  */
110
  protected function checkbox( $option_id, $disabled = false, $class_prefix = '', $label = true, $force_value = null ) {
111
  $disabled = $disabled || $this->_config->is_sealed( $option_id );
123
 
124
  if ( ! is_null( $force_value ) ) {
125
  checked( $force_value, true );
126
+ } elseif ( 'cdn.flush_manually' === $option_id ) {
127
+ checked(
128
+ $this->_config->get_boolean(
129
+ $option_id,
130
+ Cdn_Util::get_flush_manually_default_override( $this->_config->get_string( 'cdn.engine' ) )
131
+ ),
132
+ true
133
+ );
134
  } else {
135
  checked( $this->_config->get_boolean( $option_id ), true );
136
  }
145
  /**
146
  * Prints a radio button and if config value matches value
147
  *
148
+ * @param string $option_id Option id.
149
+ * @param unknown $value Value.
150
+ * @param bool $disabled Disabled flag.
151
+ * @param string $class_prefix Class prefix.
152
  */
153
  protected function radio( $option_id, $value, $disabled = false, $class_prefix = '' ) {
154
  if ( is_bool( $value ) ) {
173
  }
174
 
175
  /**
176
+ * Prints checkbox for debug option.
177
  *
178
+ * @param string $option_id Option ID.
179
  */
180
  protected function checkbox_debug( $option_id ) {
181
  if ( is_array( $option_id ) ) {
205
  echo ' />';
206
  }
207
 
208
+ /**
209
+ * Prints checkbox for debug option for pro.
210
+ *
211
+ * @param string $option_id Option ID.
212
+ * @param unknown $label Label.
213
+ * @param unknown $label_pro Pro label.
214
+ */
215
  protected function checkbox_debug_pro( $option_id, $label, $label_pro ) {
216
  if ( is_array( $option_id ) ) {
217
  $section = $option_id[0];
257
  echo '</label>';
258
  }
259
 
260
+ /**
261
+ * Prints checkbox for debug option for pro.
262
+ *
263
+ * @param string $option_id Option ID.
264
+ * @param bool $disabled Disabled flag.
265
+ * @param unknown $value_when_disabled Override value when disabled.
266
+ */
267
  protected function value_with_disabled( $option_id, $disabled, $value_when_disabled ) {
268
  if ( $disabled ) {
269
  echo 'value="' . esc_attr( $value_when_disabled ) . '" disabled="disabled" ';
272
  }
273
  }
274
 
275
+ /**
276
+ * Render header.
277
+ */
278
  protected function view() {
279
  include W3TC_INC_DIR . '/options/common/header.php';
280
  }
Cache_File.php CHANGED
@@ -156,8 +156,9 @@ class Cache_File extends Cache_Base {
156
  return array( null, $has_old_data );
157
 
158
  $fp = @fopen( $path, 'rb' );
159
- if ( !$fp )
160
  return array( null, $has_old_data );
 
161
 
162
  if ( $this->_locking )
163
  @flock( $fp, LOCK_SH );
156
  return array( null, $has_old_data );
157
 
158
  $fp = @fopen( $path, 'rb' );
159
+ if ( ! $fp || 4 > filesize( $path ) ) {
160
  return array( null, $has_old_data );
161
+ }
162
 
163
  if ( $this->_locking )
164
  @flock( $fp, LOCK_SH );
Cdn_Core.php CHANGED
@@ -727,7 +727,7 @@ class Cdn_Core {
727
  }
728
  }
729
 
730
- return ltrim( $remote_uri, '/' );
731
  }
732
 
733
  /**
727
  }
728
  }
729
 
730
+ return apply_filters( 'w3tc_uri_cdn_uri', ltrim( $remote_uri, '/' ) );
731
  }
732
 
733
  /**
Cdn_Plugin.php CHANGED
@@ -1,122 +1,104 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
4
  /**
5
- * W3 Total Cache CDN Plugin
 
 
 
6
  */
7
  class Cdn_Plugin {
8
-
9
  /**
10
- * CDN reject reason
11
  *
12
  * @var string
13
  */
14
- var $cdn_reject_reason = '';
15
 
16
  /**
17
- * Config
 
 
18
  */
19
  private $_config = null;
 
 
 
 
 
 
20
  private $_debug = false;
21
 
 
 
 
 
 
22
  private $_attachments_action = array();
23
 
24
- function __construct() {
 
 
 
25
  $this->_config = Dispatcher::config();
26
- $this->_debug = $this->_config->get_boolean( 'cdn.debug' );
27
  }
28
 
29
  /**
30
  * Runs plugin
31
  */
32
- function run() {
33
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
34
 
35
- add_filter( 'cron_schedules', array(
36
- $this,
37
- 'cron_schedules'
38
- ) );
39
-
40
- add_filter( 'w3tc_footer_comment', array(
41
- $this,
42
- 'w3tc_footer_comment'
43
- ) );
44
-
45
- if ( !Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
46
- add_action( 'w3_cdn_cron_queue_process', array(
47
- $this,
48
- 'cron_queue_process'
49
- ) );
50
-
51
- add_action( 'w3_cdn_cron_upload', array(
52
- $this,
53
- 'cron_upload'
54
- ) );
55
 
56
- add_action( 'switch_theme', array(
57
- $this,
58
- 'switch_theme'
59
- ) );
60
-
61
- add_filter( 'update_feedback', array(
62
- $this,
63
- 'update_feedback'
64
- ) );
65
 
 
66
  }
67
 
68
- $flush_on_actions = !$this->_config->get_boolean( 'cdn.flush_manually' );
 
69
 
70
  if ( $flush_on_actions ) {
71
- add_action( 'delete_attachment',
72
- array( $this, 'delete_attachment' ) );
73
-
74
- add_filter( 'wp_insert_attachment_data',
75
- array( $this, 'check_inserting_new_attachment' ), 10, 2 );
76
-
77
- add_filter( 'update_attached_file',
78
- array( $this, 'update_attached_file' ) );
79
-
80
- add_filter( 'wp_update_attachment_metadata',
81
- array( $this, 'update_attachment_metadata' ) );
82
  }
83
 
84
- add_filter( 'w3tc_preflush_cdn_all',
85
- array( $this, 'w3tc_preflush_cdn_all' ), 10, 2 );
86
-
87
- add_filter( 'w3tc_admin_bar_menu',
88
- array( $this, 'w3tc_admin_bar_menu' ) );
89
 
90
  if ( is_admin() ) {
91
- add_action( 'w3tc_config_ui_save-w3tc_cdn', array(
92
- $this, 'change_canonical_header' ), 0, 0 );
93
  add_filter( 'w3tc_module_is_running-cdn', array( $this, 'cdn_is_running' ) );
94
  }
95
 
96
- if ( !is_admin() || $this->_config->get_boolean( 'cdn.admin.media_library' ) ) {
97
- add_filter( 'wp_prepare_attachment_for_js',
98
- array( $this, 'wp_prepare_attachment_for_js' ), 0 );
99
-
100
- add_filter( 'wp_get_attachment_image_src',
101
- array( $this, 'wp_get_attachment_image_src' ), 0 );
102
  }
103
 
104
- /**
105
- * Start rewrite engine
106
- */
107
  if ( $this->can_cdn() ) {
108
  Util_Bus::add_ob_callback( 'cdn', array( $this, 'ob_callback' ) );
109
  }
110
 
111
  if ( is_admin() && Cdn_Util::can_purge( $cdn_engine ) ) {
112
- add_filter( 'media_row_actions', array(
113
- $this,
114
- 'media_row_actions'
115
- ), 0, 2 );
116
  }
117
 
118
- add_filter( 'w3tc_minify_http2_preload_url',
119
- array( $this, 'w3tc_minify_http2_preload_url' ), 3000 );
120
  }
121
 
122
  /**
@@ -124,14 +106,14 @@ class Cdn_Plugin {
124
  *
125
  * @return Cdn_Core_Admin
126
  */
127
- function get_admin() {
128
  return Dispatcher::component( 'Cdn_Core_Admin' );
129
  }
130
 
131
  /**
132
  * Cron queue process event
133
  */
134
- function cron_queue_process() {
135
  $queue_limit = $this->_config->get_integer( 'cdn.queue.limit' );
136
  return $this->get_admin()->queue_process( $queue_limit );
137
  }
@@ -139,32 +121,49 @@ class Cdn_Plugin {
139
  /**
140
  * Cron upload event
141
  */
142
- function cron_upload() {
143
  $files = $this->get_files();
144
 
145
- $upload = array();
146
  $results = array();
147
 
148
  $common = Dispatcher::component( 'Cdn_Core' );
149
 
150
  foreach ( $files as $file ) {
151
- $local_path = $common->docroot_filename_to_absolute_path( $file );
152
  $remote_path = $common->uri_to_cdn_uri( $common->docroot_filename_to_uri( $file ) );
153
- $upload[] = $common->build_file_descriptor( $local_path, $remote_path );
154
  }
155
 
156
  $common->upload( $upload, true, $results );
157
  }
158
 
159
- function check_inserting_new_attachment( $data, $postarr ) {
 
 
 
 
 
 
 
 
160
  $this->_attachments_action[ $postarr['file'] ] = empty( $postarr['ID'] ) ? 'insert' : 'update';
161
 
162
  return $data;
163
  }
164
 
 
 
 
 
 
 
 
 
165
  public function w3tc_preflush_cdn_all( $do_flush, $extras = array() ) {
166
- if ( $this->_config->get_boolean( 'cdn.flush_manually' ) ) {
167
- if ( !isset( $extras['ui_action'] ) ) {
 
168
  $do_flush = false;
169
  }
170
  }
@@ -177,19 +176,20 @@ class Cdn_Plugin {
177
  *
178
  * Upload _wp_attached_file
179
  *
180
- * @param string $attached_file
 
181
  * @return string
182
  */
183
- function update_attached_file( $attached_file ) {
184
  $common = Dispatcher::component( 'Cdn_Core' );
185
- $files = $common->get_files_for_upload( $attached_file );
186
- $files = apply_filters( 'w3tc_cdn_update_attachment', $files );
187
 
188
  $results = array();
189
 
190
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
191
  if ( Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
192
- if ( ! array_key_exists( $attached_file, $this->_attachments_action ) || $this->_attachments_action[ $attached_file ] === 'update' ) {
193
  $common->purge( $files, $results );
194
  }
195
  } else {
@@ -204,12 +204,12 @@ class Cdn_Plugin {
204
  *
205
  * Delete _wp_attached_file, _wp_attachment_metadata, _wp_attachment_backup_sizes
206
  *
207
- * @param integer $attachment_id
208
  */
209
- function delete_attachment( $attachment_id ) {
210
  $common = Dispatcher::component( 'Cdn_Core' );
211
- $files = $common->get_attachment_files( $attachment_id );
212
- $files = apply_filters( 'w3tc_cdn_delete_attachment', $files );
213
 
214
  $results = array();
215
 
@@ -226,19 +226,20 @@ class Cdn_Plugin {
226
  *
227
  * Upload _wp_attachment_metadata
228
  *
229
- * @param array $metadata
 
230
  * @return array
231
  */
232
- function update_attachment_metadata( $metadata ) {
233
  $common = Dispatcher::component( 'Cdn_Core' );
234
- $files = $common->get_metadata_files( $metadata );
235
- $files = apply_filters( 'w3tc_cdn_update_attachment_metadata', $files );
236
 
237
  $results = array();
238
 
239
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
240
  if ( Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
241
- if ($this->_config->get_boolean( 'cdn.uploads.enable' )) {
242
  $common->purge( $files, $results );
243
  }
244
  } else {
@@ -251,54 +252,66 @@ class Cdn_Plugin {
251
  /**
252
  * Cron schedules filter
253
  *
254
- * @param array $schedules
 
255
  * @return array
256
  */
257
- function cron_schedules( $schedules ) {
258
  $c = $this->_config;
259
 
260
- if ( $c->get_boolean( 'cdn.enabled' ) &&
261
- !Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
262
  $queue_interval = $c->get_integer( 'cdn.queue.interval' );
 
263
  $schedules['w3_cdn_cron_queue_process'] = array(
264
- 'interval' => $queue_interval,
265
- 'display' => sprintf(
266
- '[W3TC] CDN queue process (every %d seconds)', $queue_interval
267
- )
268
- );
 
 
 
 
 
269
  }
270
 
271
  if ( $c->get_boolean( 'cdn.enabled' ) &&
272
  $c->get_boolean( 'cdn.autoupload.enabled' ) &&
273
- !Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
274
  $autoupload_interval = $c->get_integer( 'cdn.autoupload.interval' );
 
275
  $schedules['w3_cdn_cron_upload'] = array(
276
- 'interval' => $autoupload_interval,
277
- 'display' => sprintf(
278
- '[W3TC] CDN auto upload (every %d seconds)', $autoupload_interval
279
- )
280
- );
 
 
 
 
 
281
  }
282
 
283
  return $schedules;
284
  }
285
 
286
  /**
287
- * Switch theme action
288
  */
289
- function switch_theme() {
290
  $state = Dispatcher::config_state();
291
  $state->set( 'cdn.show_note_theme_changed', true );
292
  $state->save();
293
  }
294
 
295
  /**
296
- * WP Upgrade action hack
297
  *
298
- * @param string $message
299
  */
300
- function update_feedback( $message ) {
301
- if ( $message == __( 'Upgrading database' ) ) {
302
  $state = Dispatcher::config_state();
303
  $state->set( 'cdn.show_note_wp_upgraded', true );
304
  $state->save();
@@ -306,21 +319,21 @@ class Cdn_Plugin {
306
  }
307
 
308
  /**
309
- * OB Callback
 
 
310
  *
311
- * @param string $buffer
312
  * @return string
313
  */
314
- function ob_callback( $buffer ) {
315
- if ( $buffer != '' && Util_Content::is_html_xml( $buffer ) ) {
316
  if ( $this->can_cdn2( $buffer ) ) {
317
  $srcset_helper = new _Cdn_Plugin_ContentFilter();
318
- $buffer = $srcset_helper->replace_all_links( $buffer );
319
 
320
  if ( $this->_debug ) {
321
  $replaced_urls = $srcset_helper->get_replaced_urls();
322
- $buffer = $this->w3tc_footer_comment_after(
323
- $buffer, $replaced_urls );
324
  }
325
  }
326
  }
@@ -329,11 +342,11 @@ class Cdn_Plugin {
329
  }
330
 
331
  /**
332
- * Returns array of files to upload
333
  *
334
  * @return array
335
  */
336
- function get_files() {
337
  $files = array();
338
 
339
  if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
@@ -360,13 +373,15 @@ class Cdn_Plugin {
360
  *
361
  * @return array
362
  */
363
- function get_files_includes() {
364
  $includes_root = Util_Environment::normalize_path( ABSPATH . WPINC );
365
- $doc_root = Util_Environment::normalize_path( Util_Environment::document_root() );
366
  $includes_path = ltrim( str_replace( $doc_root, '', $includes_root ), '/' );
367
 
368
  $files = Cdn_Util::search_files(
369
- $includes_root, $includes_path, $this->_config->get_string( 'cdn.includes.files' )
 
 
370
  );
371
 
372
  return $files;
@@ -377,69 +392,66 @@ class Cdn_Plugin {
377
  *
378
  * @return array
379
  */
380
- function get_files_theme() {
381
- /**
382
- * If mobile or referrer support enabled
383
- * we should upload whole themes directory
384
- */
385
- if ( $this->_config->get_boolean( 'mobile.enabled' )
386
- || $this->_config->get_boolean( 'referrer.enabled' ) ) {
387
  $themes_root = get_theme_root();
388
  } else {
389
  $themes_root = get_stylesheet_directory();
390
  }
391
 
392
  $themes_root = Util_Environment::normalize_path( $themes_root );
393
- $themes_path = ltrim( str_replace(
394
- Util_Environment::normalize_path( Util_Environment::document_root() ), '', $themes_root ), '/' );
395
- $files = Cdn_Util::search_files(
396
- $themes_root, $themes_path, $this->_config->get_string( 'cdn.theme.files' )
 
397
  );
398
 
399
  return $files;
400
  }
401
 
402
  /**
403
- * Exports min files to CDN
404
  *
405
  * @return array
406
  */
407
- function get_files_minify() {
408
  $files = array();
409
 
410
  if ( $this->_config->get_boolean( 'minify.rewrite' ) &&
411
  Util_Rule::can_check_rules() &&
412
- ( !$this->_config->get_boolean( 'minify.auto' ) ||
413
- Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) ) ) {
414
-
 
415
 
416
  $minify = Dispatcher::component( 'Minify_Plugin' );
417
 
418
- $document_root = Util_Environment::normalize_path(
419
- Util_Environment::document_root() );
420
- $minify_root = Util_Environment::normalize_path(
421
- Util_Environment::cache_blog_dir( 'minify' ) );
422
- $minify_path = ltrim( str_replace( $document_root, '', $minify_root ), '/' );
423
  $urls = $minify->get_urls();
424
 
425
- // in WPMU + network admin (this code used for minify manual only)
426
  // common minify files are stored under context of main blog (i.e. 1)
427
- // but have urls of 0 blog, so download has to be used
428
- if ( $this->_config->get_string( 'minify.engine' ) == 'file' &&
429
- !( Util_Environment::is_wpmu() && is_network_admin() ) ) {
430
-
431
  foreach ( $urls as $url ) {
432
  Util_Http::get( $url );
433
  }
434
 
435
- $files = Cdn_Util::search_files( $minify_root,
436
- $minify_path, '*.css;*.js' );
437
-
 
 
438
  } else {
439
  foreach ( $urls as $url ) {
440
  $file = Util_Environment::normalize_file_minify( $url );
441
 
442
- if ( !Util_Environment::is_url( $file ) ) {
443
  $file = $document_root . '/' . $file;
444
  $file = ltrim( str_replace( $minify_root, '', $file ), '/' );
445
 
@@ -465,47 +477,47 @@ class Cdn_Plugin {
465
  *
466
  * @return array
467
  */
468
- function get_files_custom() {
469
- $files = array();
470
- $document_root = Util_Environment::normalize_path(
471
- Util_Environment::document_root() );
472
- $custom_files = $this->_config->get_array( 'cdn.custom.files' );
473
- $custom_files = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $custom_files );
474
- $site_root = Util_Environment::normalize_path( Util_Environment::site_root() );
475
- $path = Util_Environment::site_url_uri();
476
  $site_root_dir = str_replace( $document_root, '', $site_root );
477
  if ( strstr( WP_CONTENT_DIR, Util_Environment::site_root() ) === false ) {
478
  $site_root = Util_Environment::normalize_path( Util_Environment::document_root() );
479
- $path = '';
480
  }
481
 
482
  $content_path = trim( str_replace( WP_CONTENT_DIR, '', $site_root ), '/\\' );
483
 
484
  foreach ( $custom_files as $custom_file ) {
485
- if ( $custom_file != '' ) {
486
  $custom_file = Cdn_Util::replace_folder_placeholders( $custom_file );
487
  $custom_file = Util_Environment::normalize_file( $custom_file );
488
 
489
- if ( !Util_Environment::is_wpmu() ) {
490
- $dir = trim( dirname( $custom_file ), '/\\' );
491
- $rel_path = trim( dirname( $custom_file ), '/\\' );
492
- } else
493
- $rel_path = $dir = trim( dirname( $custom_file ), '/\\' );
494
 
495
- if ( strpos( $dir, '<currentblog>' ) != false ) {
496
- $rel_path = $dir = str_replace(
497
- '<currentblog>', 'blogs.dir/'
498
- . Util_Environment::blog_id(), $dir
499
- );
500
  }
501
 
502
- if ( $dir == '.' ) {
503
- $rel_path = $dir = '';
 
504
  }
505
- $mask = basename( $custom_file );
 
506
  $files = array_merge(
507
- $files, Cdn_Util::search_files( $document_root . '/'
508
- . $dir, $rel_path, $mask )
 
 
 
 
509
  );
510
  }
511
  }
@@ -518,48 +530,38 @@ class Cdn_Plugin {
518
  *
519
  * @return boolean
520
  */
521
- function can_cdn() {
522
- /**
523
- * Skip if admin
524
- */
525
  if ( defined( 'WP_ADMIN' ) ) {
526
- $this->cdn_reject_reason = 'wp-admin';
527
 
528
  return false;
529
  }
530
 
531
- /**
532
- * Check for WPMU's and WP's 3.0 short init
533
- */
534
  if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
535
- $this->cdn_reject_reason = 'Short init';
536
 
537
  return false;
538
  }
539
 
540
- /**
541
- * Check User agent
542
- */
543
- if ( !$this->check_ua() ) {
544
- $this->cdn_reject_reason = 'user agent is rejected';
545
 
546
  return false;
547
  }
548
 
549
- /**
550
- * Check request URI
551
- */
552
- if ( !$this->_check_request_uri() ) {
553
- $this->cdn_reject_reason = 'request URI is rejected';
554
 
555
  return false;
556
  }
557
 
558
- /**
559
- * Do not replace urls if SSL and SSL support is do not replace
560
- */
561
  if ( Util_Environment::is_https() && $this->_config->get_boolean( 'cdn.reject.ssl' ) ) {
562
- $this->cdn_reject_reason = 'SSL is rejected';
563
 
564
  return false;
565
  }
@@ -570,26 +572,21 @@ class Cdn_Plugin {
570
  /**
571
  * Returns true if we can do CDN logic
572
  *
573
- * @param unknown $buffer
 
574
  * @return string
575
  */
576
- function can_cdn2( $buffer ) {
577
- /**
578
- * Check for DONOTCDN constant
579
- */
580
  if ( defined( 'DONOTCDN' ) && DONOTCDN ) {
581
- $this->cdn_reject_reason = 'DONOTCDN constant is defined';
582
 
583
  return false;
584
  }
585
 
586
- /**
587
- * Check logged users roles
588
- */
589
- if ( $this->_config->get_boolean(
590
- 'cdn.reject.logged_roles' ) && !$this->_check_logged_in_role_allowed()
591
- ) {
592
- $this->cdn_reject_reason = 'logged in role is rejected';
593
 
594
  return false;
595
  }
@@ -598,17 +595,18 @@ class Cdn_Plugin {
598
  }
599
 
600
  /**
601
- * Checks User Agent
602
  *
603
  * @return boolean
604
  */
605
- function check_ua() {
606
- $uas = array_merge( $this->_config->get_array( 'cdn.reject.ua' ), array(
607
- W3TC_POWERED_BY
608
- ) );
 
609
 
610
  foreach ( $uas as $ua ) {
611
- if ( !empty( $ua ) ) {
612
  if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), $ua ) !== false ) {
613
  return false;
614
  }
@@ -619,11 +617,11 @@ class Cdn_Plugin {
619
  }
620
 
621
  /**
622
- * Checks request URI
623
  *
624
  * @return boolean
625
  */
626
- function _check_request_uri() {
627
  $reject_uri = $this->_config->get_array( 'cdn.reject.uri' );
628
  $reject_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $reject_uri );
629
 
@@ -631,14 +629,14 @@ class Cdn_Plugin {
631
  $expr = trim( $expr );
632
  $expr = str_replace( '~', '\~', $expr );
633
 
634
- if ( $expr != '' && preg_match( '~' . $expr . '~i', isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' ) ) {
635
  return false;
636
  }
637
  }
638
 
639
-
640
- if ( Util_Request::get_string( 'wp_customize' ) )
641
  return false;
 
642
 
643
  return true;
644
  }
@@ -650,42 +648,45 @@ class Cdn_Plugin {
650
  private function _check_logged_in_role_allowed() {
651
  $current_user = wp_get_current_user();
652
 
653
- if ( !is_user_logged_in() )
654
  return true;
 
655
 
656
  $roles = $this->_config->get_array( 'cdn.reject.roles' );
657
 
658
- if ( empty( $roles ) || empty( $current_user->roles ) ||
659
- !is_array( $current_user->roles ) )
660
  return true;
 
661
 
662
  foreach ( $current_user->roles as $role ) {
663
- if ( in_array( $role, $roles ) )
664
  return false;
 
665
  }
666
 
667
  return true;
668
  }
669
 
670
  /**
671
- * media_row_actions filter
 
 
 
672
  *
673
- * @param array $actions
674
- * @param object $post
675
  * @return array
676
  */
677
- function media_row_actions( $actions, $post ) {
678
  return $this->get_admin()->media_row_actions( $actions, $post );
679
  }
680
 
681
-
682
  /**
 
683
  *
 
684
  *
685
- * @param unknown $current_state
686
  * @return bool
687
  */
688
- function cdn_is_running( $current_state ) {
689
  $admin = $this->get_admin();
690
  return $admin->is_running();
691
  }
@@ -693,24 +694,24 @@ class Cdn_Plugin {
693
  /**
694
  * Change canonical header
695
  */
696
- function change_canonical_header() {
697
  $admin = $this->get_admin();
698
  $admin->change_canonical_header();
699
  }
700
 
701
  /**
702
- * Adjusts attachment urls to cdn. This is for those who rely on
703
- * wp_prepare_attachment_for_js()
 
704
  *
705
- * @param array $response Mixed collection of data about the attachment object
706
- * @return array
707
  */
708
  public function wp_prepare_attachment_for_js( $response ) {
709
- $response['url'] = $this->wp_prepare_attachment_for_js_url( $response['url'] );
710
  $response['link'] = $this->wp_prepare_attachment_for_js_url( $response['link'] );
711
 
712
- if ( !empty( $response['sizes'] ) ) {
713
- foreach( $response['sizes'] as $size => &$data ) {
714
  $data['url'] = $this->wp_prepare_attachment_for_js_url( $data['url'] );
715
  }
716
  }
@@ -719,62 +720,38 @@ class Cdn_Plugin {
719
  }
720
 
721
  /**
722
- * An attachment's local url to modify into a cdn url
723
  *
724
- * @param string $url the local url to modify
725
- * @return string
 
726
  */
727
  private function wp_prepare_attachment_for_js_url( $url ) {
728
  $url = trim( $url );
729
- if ( !empty( $url ) ) {
730
- $parsed = parse_url( $url );
731
- $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
732
- ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
733
-
734
- $wp_upload_dir = wp_upload_dir();
735
- $upload_base_url = $wp_upload_dir['baseurl'];
736
-
737
- if ( substr($url, 0, strlen( $upload_base_url ) ) == $upload_base_url ) {
738
- $common = Dispatcher::component( 'Cdn_Core' );
739
- $new_url = $common->url_to_cdn_url( $url, $uri );
740
- if ( !is_null( $new_url ) ) {
741
- $url = $new_url;
742
- }
743
- }
744
- }
745
-
746
- return $url;
747
- }
748
-
749
- /**
750
- * Adjusts attachment image src to cdn. This is for those who rely on
751
- * wp_get_attachment_image_src()
752
- *
753
- * @param array $image Image object
754
- * @return array
755
- */
756
- function wp_get_attachment_image_src( $image ) {
757
- $url = empty( $image[0] ) ? null : trim( $image[0] );
758
-
759
  if ( ! empty( $url ) ) {
760
- $parsed = parse_url( $url );
761
- $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) . ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
 
 
762
  $wp_upload_dir = wp_upload_dir();
763
  $upload_base_url = $wp_upload_dir['baseurl'];
 
764
  if ( substr( $url, 0, strlen( $upload_base_url ) ) === $upload_base_url ) {
765
  $common = Dispatcher::component( 'Cdn_Core' );
766
  $new_url = $common->url_to_cdn_url( $url, $uri );
767
  if ( ! is_null( $new_url ) ) {
768
- $image[0] = $new_url;
769
  }
770
  }
771
  }
772
 
773
- return $image;
774
  }
775
 
776
  /**
777
  * Mutate http/2 header links
 
 
778
  */
779
  public function w3tc_minify_http2_preload_url( $data ) {
780
  $url = $data['result_link'];
@@ -784,60 +761,73 @@ class Cdn_Plugin {
784
  return $data;
785
  }
786
 
787
- $parsed = parse_url( $url );
788
- $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
789
- ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
790
 
791
- $common = Dispatcher::component( 'Cdn_Core' );
792
  $new_url = $common->url_to_cdn_url( $url, $uri );
793
  if ( is_null( $new_url ) ) {
794
  return $data;
795
  }
796
 
797
  $data['result_link'] = $new_url;
798
- // url_to_cdn_url processed by browsercache internally
 
799
  $data['browsercache_processed'] = '*';
800
 
801
  return $data;
802
  }
803
 
 
 
 
 
 
804
  public function w3tc_admin_bar_menu( $menu_items ) {
805
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
806
 
807
  if ( Cdn_Util::can_purge_all( $cdn_engine ) ) {
808
  $menu_items['20710.cdn'] = array(
809
- 'id' => 'w3tc_cdn_flush_all',
810
  'parent' => 'w3tc_flush',
811
- 'title' => __( 'CDN: All', 'w3-total-cache' ),
812
- 'href' => wp_nonce_url( admin_url(
813
- 'admin.php?page=w3tc_cdn&amp;w3tc_flush_cdn' ),
814
- 'w3tc' )
815
  );
816
  }
817
 
818
  if ( Cdn_Util::can_purge( $cdn_engine ) ) {
819
  $menu_items['20790.cdn'] = array(
820
- 'id' => 'w3tc_cdn_flush',
821
  'parent' => 'w3tc_flush',
822
- 'title' => __( 'CDN: Manual Purge', 'w3-total-cache' ),
823
- 'href' => wp_nonce_url( admin_url( 'admin.php?page=w3tc_cdn&amp;w3tc_cdn_purge' ), 'w3tc' ),
824
- 'meta' => array( 'onclick' => "w3tc_popupadmin_bar(this.href); return false" )
825
  );
826
  }
827
 
828
  return $menu_items;
829
  }
830
 
 
 
 
 
 
831
  public function w3tc_footer_comment( $strings ) {
832
  $common = Dispatcher::component( 'Cdn_Core' );
833
- $cdn = $common->get_cdn();
834
- $via = $cdn->get_via();
835
 
836
  $strings[] = sprintf(
837
- __( 'Content Delivery Network via %s%s', 'w3-total-cache' ),
 
 
 
 
838
  ( $via ? $via : 'N/A' ),
839
- ( empty( $this->cdn_reject_reason ) ? '' :
840
- sprintf( ' (%s)', $this->cdn_reject_reason ) ) );
841
 
842
  if ( $this->_debug ) {
843
  $strings[] = '{w3tc_cdn_debug_info}';
@@ -846,35 +836,68 @@ class Cdn_Plugin {
846
  return $strings;
847
  }
848
 
849
-
850
-
 
 
 
 
851
  public function w3tc_footer_comment_after( $buffer, $replaced_urls ) {
852
  $strings = array();
853
 
854
- if ( is_array( $replaced_urls ) &&
855
- count( $replaced_urls ) ) {
856
- $strings[] = "Replaced URLs for CDN:";
857
 
858
  foreach ( $replaced_urls as $old_url => $new_url ) {
859
- $strings[] = sprintf( "%s => %s",
 
860
  Util_Content::escape_comment( $old_url ),
861
- Util_Content::escape_comment( $new_url ) );
 
862
  }
863
 
864
  $strings[] = '';
865
  }
866
 
867
- $buffer = str_replace( '{w3tc_cdn_debug_info}',
868
- implode( "\n", $strings ), $buffer );
869
  return $buffer;
870
  }
871
  }
872
 
873
- class _Cdn_Plugin_ContentFilter {
874
-
 
 
 
 
 
 
 
 
 
 
875
  private $_regexps = array();
 
 
 
 
 
 
876
  private $_placeholders = array();
 
 
 
 
 
 
877
  private $_config;
 
 
 
 
 
 
878
  private $_replaced_urls = array();
879
 
880
  /**
@@ -884,21 +907,35 @@ class _Cdn_Plugin_ContentFilter {
884
  */
885
  private static $_upload_scheduled = false;
886
 
887
- function __construct() {
 
 
 
888
  $this->_config = Dispatcher::config();
889
  }
890
 
891
- function replace_all_links( $buffer ) {
 
 
 
 
 
 
 
892
  $this->fill_regexps();
893
 
894
  $srcset_pattern = '~srcset\s*=\s*[\"\'](.*?)[\"\']~';
895
- $buffer = preg_replace_callback(
896
- $srcset_pattern, array( $this, '_srcset_replace_callback' ), $buffer
 
 
897
  );
898
 
899
  foreach ( $this->_regexps as $regexp ) {
900
  $buffer = preg_replace_callback(
901
- $regexp, array( $this, '_link_replace_callback' ), $buffer
 
 
902
  );
903
  }
904
 
@@ -912,14 +949,12 @@ class _Cdn_Plugin_ContentFilter {
912
  $processor = array( $this, '_minify_auto_pushcdn_link_replace_callback' );
913
  }
914
  } else {
915
- $minify_url_regexp = $this->minify_url_regexp(
916
- '/[a-z0-9]+\..+\.include(-(footer|body))?(-nb)?\.[a-f0-9]+\.(css|js)' );
917
-
918
- $processor = array( $this, '_link_replace_callback' );
919
  }
920
 
921
- if ( !empty( $minify_url_regexp ) ) {
922
- $regexp = '~(["\'(=])\s*' . $minify_url_regexp .'~U';
923
  $buffer = preg_replace_callback( $regexp, $processor, $buffer );
924
  }
925
  }
@@ -930,15 +965,17 @@ class _Cdn_Plugin_ContentFilter {
930
  }
931
 
932
  /**
933
- * Link replace callback
 
 
934
  *
935
- * @param array $matches
936
  * @return string
937
  */
938
- function _link_replace_callback( $matches ) {
939
  list( $match, $quote, $url, , , , $path ) = $matches;
 
940
  $path = ltrim( $path, '/' );
941
- $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
942
  if ( is_null( $r ) ) {
943
  $r = $this->_link_replace_callback_ask_cdn( $match, $quote, $url, $path );
944
  }
@@ -946,42 +983,61 @@ class _Cdn_Plugin_ContentFilter {
946
  return $r;
947
  }
948
 
949
- function _srcset_replace_callback( $matches ) {
 
 
 
 
 
 
 
950
  list( $match, $srcset ) = $matches;
951
- if ( empty( $this->_regexps ) )
 
952
  return $match;
953
- $index = "%srcset-" . count( $this->_placeholders ) . "%";
954
 
955
- $srcset_urls = explode( ',', $srcset );
 
 
956
  $new_srcset_urls = array();
957
 
958
  foreach ( $srcset_urls as $set ) {
959
-
960
- preg_match( "~(?P<spaces>^\s*)(?P<url>\S+)(?P<rest>.*)~", $set, $parts );
961
  if ( isset( $parts['url'] ) ) {
962
-
963
  foreach ( $this->_regexps as $regexp ) {
964
- $new_url = preg_replace_callback( $regexp, array(
 
 
965
  $this,
966
- '_link_replace_callback'
967
- ), '"' . $parts['url'] . '">' );
 
 
968
 
969
- if ( '"' . $parts['url'] . '">' != $new_url ) {
970
  $parts['url'] = substr( $new_url, 1, -2 );
971
  break;
972
  }
973
  }
974
- $new_srcset_urls[] = $parts['spaces'] .$parts['url']
975
- . $parts['rest'];
976
  } else {
977
  $new_srcset_urls[] = $set;
978
  }
979
-
980
  }
981
- $this->_placeholders[$index] = implode( ',', $new_srcset_urls );
 
 
982
  return 'srcset="' . $index . '"';
983
  }
984
 
 
 
 
 
 
 
 
985
  private function replace_placeholders( $buffer ) {
986
  foreach ( $this->_placeholders as $srcset_id => $srcset_content ) {
987
  $buffer = str_replace( $srcset_id, $srcset_content, $buffer );
@@ -992,20 +1048,27 @@ class _Cdn_Plugin_ContentFilter {
992
  /**
993
  * Gets regexp for minified files
994
  *
 
 
995
  * @return string
996
  */
997
  private function minify_url_regexp( $filename_mask ) {
998
  $minify_base_url = Util_Environment::filename_to_url(
999
  Util_Environment::cache_blog_minify_dir()
1000
  );
1001
- $matches = null;
1002
- if ( !preg_match( '~((https?://)?([^/]+))(.+)~i', $minify_base_url, $matches ) ) {
1003
- error_log( 'cant find minification base url, make sure minification folder sits inside WP_CONTENT_DIR and DOCUMENT_ROOT is set correctly' );
 
 
 
 
 
1004
  return '';
1005
  }
1006
 
1007
  $protocol_domain_regexp = Util_Environment::get_url_regexp( $matches[1] );
1008
- $path_regexp = Util_Environment::preg_quote( $matches[4] );
1009
 
1010
  $regexp =
1011
  '(' .
@@ -1016,49 +1079,51 @@ class _Cdn_Plugin_ContentFilter {
1016
  }
1017
 
1018
  /**
 
1019
  *
 
 
 
 
1020
  *
1021
- * @param unknown $domain_url_regexp
1022
- * @param unknown $baseurl
1023
- * @param unknown $upload_info
1024
- * @param unknown $regexps
1025
  * @return array
1026
  */
1027
- private function make_uploads_regexes( $domain_url_regexp, $baseurl,
1028
- $upload_info, $regexps ) {
1029
  if ( preg_match( '~' . $domain_url_regexp . '~i', $baseurl ) ) {
1030
  $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?('
1031
  . Util_Environment::preg_quote( $upload_info['baseurlpath'] )
1032
  . '([^"\')>]+)))~i';
1033
  } else {
1034
- $parsed = @parse_url( $baseurl );
1035
- $upload_url_domain_regexp = isset( $parsed['host'] )
1036
- ? Util_Environment::get_url_regexp( $parsed['scheme'] . '://'
1037
- . $parsed['host'] ) : $domain_url_regexp;
1038
- $baseurlpath = isset( $parsed['path'] ) ? rtrim( $parsed['path'], '/' ) : '';
1039
- if ( $baseurlpath )
1040
  $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp . ')?('
1041
  . Util_Environment::preg_quote( $baseurlpath )
1042
  . '([^"\'>]+)))~i';
1043
- else
1044
  $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp
1045
  . ')(([^"\'>]+)))~i';
 
1046
  }
1047
  return $regexps;
1048
  }
1049
 
 
 
 
1050
  private function fill_regexps() {
1051
  $regexps = array();
1052
 
1053
- $site_path = Util_Environment::site_url_uri();
1054
  $domain_url_regexp = Util_Environment::home_domain_root_url_regexp();
1055
 
1056
  $site_domain_url_regexp = false;
1057
- if ( $domain_url_regexp != Util_Environment::get_url_regexp(
1058
- Util_Environment::url_to_host( site_url() ) ) )
1059
- $site_domain_url_regexp = Util_Environment::get_url_regexp(
1060
- Util_Environment::url_to_host( site_url() )
1061
- );
1062
 
1063
  if ( $this->_config->get_boolean( 'cdn.uploads.enable' ) ) {
1064
  $upload_info = Util_Http::upload_info();
@@ -1067,56 +1132,59 @@ class _Cdn_Plugin_ContentFilter {
1067
  $baseurl = $upload_info['baseurl'];
1068
 
1069
  if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
1070
- $parsed = @parse_url( $upload_info['baseurl'] );
1071
  $baseurl = home_url() . $parsed['path'];
1072
  }
1073
 
1074
  $regexps = $this->make_uploads_regexes(
1075
- $domain_url_regexp, $baseurl, $upload_info, $regexps
 
 
 
1076
  );
1077
- if ( $site_domain_url_regexp )
 
1078
  $regexps = $this->make_uploads_regexes(
1079
- $site_domain_url_regexp, $baseurl, $upload_info, $regexps
 
 
 
1080
  );
 
1081
  }
1082
  }
1083
 
1084
  if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
1085
  $mask = $this->_config->get_string( 'cdn.includes.files' );
1086
- if ( $mask != '' ) {
1087
- $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp .
1088
- ')?(' .
1089
  Util_Environment::preg_quote( $site_path . WPINC ) .
1090
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
1091
- if ( $site_domain_url_regexp )
1092
- $regexps[] = '~(["\'(=])\s*((' .
1093
- $site_domain_url_regexp . ')?(' .
1094
  Util_Environment::preg_quote( $site_path . WPINC ) .
1095
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
1096
  ')([^"\'() >]*)))~i';
 
1097
  }
1098
  }
1099
 
1100
  if ( $this->_config->get_boolean( 'cdn.theme.enable' ) ) {
1101
- $theme_dir = preg_replace( '~'
1102
- . $domain_url_regexp . '~i', '', get_theme_root_uri() );
1103
 
1104
  $mask = $this->_config->get_string( 'cdn.theme.files' );
1105
 
1106
- if ( $mask != '' ) {
1107
  $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?(' .
1108
  Util_Environment::preg_quote( $theme_dir ) . '/(' .
1109
  Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
1110
  if ( $site_domain_url_regexp ) {
1111
- $theme_dir2 = preg_replace( '~' . $site_domain_url_regexp
1112
- . '~i', '', get_theme_root_uri() );
1113
- $regexps[] = '~(["\'(=])\s*((' .
1114
- $site_domain_url_regexp . ')?(' .
1115
  Util_Environment::preg_quote( $theme_dir ) . '/(' .
1116
  Cdn_Util::get_regexp_by_mask( $mask ) .
1117
  ')([^"\'() >]*)))~i';
1118
- $regexps[] = '~(["\'(=])\s*((' .
1119
- $site_domain_url_regexp . ')?(' .
1120
  Util_Environment::preg_quote( $theme_dir2 ) .
1121
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
1122
  ')([^"\'() >]*)))~i';
@@ -1130,25 +1198,24 @@ class _Cdn_Plugin_ContentFilter {
1130
  $masks = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $masks );
1131
 
1132
  if ( count( $masks ) ) {
1133
- $custom_regexps_urls = array();
1134
- $custom_regexps_uris = array();
1135
  $custom_regexps_docroot_related = array();
1136
 
1137
  foreach ( $masks as $mask ) {
1138
- if ( !empty( $mask ) ) {
1139
  if ( Util_Environment::is_url( $mask ) ) {
1140
  $url_match = array();
1141
  if ( preg_match( '~^((https?:)?//([^/]*))(.*)~', $mask, $url_match ) ) {
1142
  $custom_regexps_urls[] = array(
1143
- 'domain_url' => Util_Environment::get_url_regexp(
1144
- $url_match[1] ),
1145
- 'uri' => Cdn_Util::get_regexp_by_mask( $url_match[4] )
1146
  );
1147
  }
1148
- } elseif ( substr( $mask, 0, 1 ) == '/' ) { // uri
1149
  $custom_regexps_uris[] = Cdn_Util::get_regexp_by_mask( $mask );
1150
  } else {
1151
- $file = Util_Environment::normalize_path( $mask ); // \ -> backspaces
1152
  $file = str_replace( Util_Environment::site_root(), '', $file );
1153
  $file = ltrim( $file, '/' );
1154
 
@@ -1169,14 +1236,14 @@ class _Cdn_Plugin_ContentFilter {
1169
  }
1170
 
1171
  if ( count( $custom_regexps_docroot_related ) > 0 ) {
1172
- $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp .
1173
- ')?(' . Util_Environment::preg_quote( $site_path ) .
1174
  '(' . implode( '|', $custom_regexps_docroot_related ) . ')([^"\'() >]*)))~i';
1175
- if ( $site_domain_url_regexp )
1176
- $regexps[] = '~(["\'(=])\s*((' .
1177
- $site_domain_url_regexp . ')?(' .
1178
  Util_Environment::preg_quote( $site_path ) . '(' .
1179
  implode( '|', $custom_regexps_docroot_related ) . ')([^"\'() >]*)))~i';
 
1180
  }
1181
  }
1182
  }
@@ -1185,40 +1252,40 @@ class _Cdn_Plugin_ContentFilter {
1185
  }
1186
 
1187
  /**
1188
- * Link replace callback, basic checks step
 
 
 
 
 
1189
  *
1190
- * @param string $match
1191
- * @param string $quote
1192
- * @param string $url
1193
- * @param string $path
1194
  * @return null|string
1195
  */
1196
- function _link_replace_callback_checks( $match, $quote, $url, $path ) {
1197
  global $wpdb;
 
1198
  static $queue = null, $reject_files = null;
1199
 
1200
  /**
1201
  * Check if URL was already replaced
1202
  */
1203
- if ( isset( $this->_replaced_urls[$url] ) ) {
1204
- return $quote . $this->_replaced_urls[$url];
1205
  }
1206
 
1207
  /**
1208
  * Check URL for rejected files
1209
  */
1210
- if ( $reject_files === null ) {
1211
  $reject_files = $this->_config->get_array( 'cdn.reject.files' );
1212
  }
1213
 
1214
  foreach ( $reject_files as $reject_file ) {
1215
- if ( $reject_file != '' ) {
1216
  $reject_file = Cdn_Util::replace_folder_placeholders( $reject_file );
1217
-
1218
  $reject_file = Util_Environment::normalize_file( $reject_file );
1219
 
1220
- $reject_file_regexp = '~^('
1221
- . Cdn_Util::get_regexp_by_mask( $reject_file ) . ')~i';
1222
 
1223
  if ( preg_match( $reject_file_regexp, $path ) ) {
1224
  return $match;
@@ -1226,40 +1293,45 @@ class _Cdn_Plugin_ContentFilter {
1226
  }
1227
  }
1228
 
1229
- /**
1230
- * Don't replace URL for files that are in the CDN queue
1231
- */
1232
- if ( $queue === null ) {
1233
- if ( !Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) ) {
1234
- $sql = $wpdb->prepare( 'SELECT remote_path FROM '
1235
- . $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE
1236
- . ' WHERE remote_path = %s', $path );
1237
- $queue = $wpdb->get_var( $sql );
1238
- }
1239
- else {
 
 
1240
  $queue = false;
1241
  }
1242
  }
 
1243
  if ( $queue ) {
1244
  return $match;
1245
  }
 
1246
  return null;
1247
  }
1248
 
1249
  /**
1250
  * Link replace callback, url replacement using cdn engine
1251
  *
1252
- * @param string $match
1253
- * @param string $quote
1254
- * @param string $url
1255
- * @param string $path
 
1256
  * @return null|string
1257
  */
1258
- function _link_replace_callback_ask_cdn( $match, $quote, $url, $path ) {
1259
- $common = Dispatcher::component( 'Cdn_Core' );
1260
  $new_url = $common->url_to_cdn_url( $url, $path );
1261
- if ( !is_null( $new_url ) ) {
1262
- $this->_replaced_urls[$url] = $new_url;
1263
  return $quote . $new_url;
1264
  }
1265
 
@@ -1267,31 +1339,30 @@ class _Cdn_Plugin_ContentFilter {
1267
  }
1268
 
1269
  /**
1270
- * Link replace callback for urls from minify module using auto mode and in cdn of push type
 
 
1271
  *
1272
- * @param array $matches
1273
  * @return string
1274
  */
1275
- function _minify_auto_pushcdn_link_replace_callback( $matches ) {
1276
  static $dispatcher = null;
1277
 
1278
  list( $match, $quote, $url, , , , $path ) = $matches;
 
1279
  $path = ltrim( $path, '/' );
1280
- $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
1281
 
1282
- /**
1283
- * Check if we can replace that URL (for auto mode it should be uploaded)
1284
- */
1285
- if ( !Dispatcher::is_url_cdn_uploaded( $url ) ) {
1286
  Dispatcher::component( 'Cdn_Core' )->queue_upload_url( $url );
1287
- if ( !self::$_upload_scheduled ) {
1288
  wp_schedule_single_event( time(), 'w3_cdn_cron_queue_process' );
1289
  add_action( 'shutdown', 'wp_cron' );
1290
 
1291
  self::$_upload_scheduled = true;
1292
  }
1293
 
1294
-
1295
  return $match;
1296
  }
1297
 
@@ -1301,7 +1372,12 @@ class _Cdn_Plugin_ContentFilter {
1301
  return $r;
1302
  }
1303
 
1304
- function get_replaced_urls() {
 
 
 
 
 
1305
  return $this->_replaced_urls;
1306
  }
1307
  }
1
  <?php
2
+ /**
3
+ * File: Cdn_Plugin.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
  /**
11
+ * Class: Cdn_Plugin
12
+ *
13
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
14
+ * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
15
  */
16
  class Cdn_Plugin {
 
17
  /**
18
+ * Reject reason.
19
  *
20
  * @var string
21
  */
22
+ private $cdn_reject_reason = '';
23
 
24
  /**
25
+ * Config.
26
+ *
27
+ * @var Config
28
  */
29
  private $_config = null;
30
+
31
+ /**
32
+ * Debug flag.
33
+ *
34
+ * @var bool
35
+ */
36
  private $_debug = false;
37
 
38
+ /**
39
+ * Attachements action.
40
+ *
41
+ * @var array
42
+ */
43
  private $_attachments_action = array();
44
 
45
+ /**
46
+ * Constructor.
47
+ */
48
+ public function __construct() {
49
  $this->_config = Dispatcher::config();
50
+ $this->_debug = $this->_config->get_boolean( 'cdn.debug' );
51
  }
52
 
53
  /**
54
  * Runs plugin
55
  */
56
+ public function run() {
57
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
58
 
59
+ add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
60
+ add_filter( 'w3tc_footer_comment', array( $this, 'w3tc_footer_comment' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ if ( ! Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
63
+ add_action( 'w3_cdn_cron_queue_process', array( $this, 'cron_queue_process' ) );
64
+ add_action( 'w3_cdn_cron_upload', array( $this, 'cron_upload' ) );
65
+ add_action( 'switch_theme', array( $this, 'switch_theme' ) );
 
 
 
 
 
66
 
67
+ add_filter( 'update_feedback', array( $this, 'update_feedback' ) );
68
  }
69
 
70
+ $default_override = Cdn_Util::get_flush_manually_default_override( $cdn_engine );
71
+ $flush_on_actions = ! $this->_config->get_boolean( 'cdn.flush_manually', $default_override );
72
 
73
  if ( $flush_on_actions ) {
74
+ add_action( 'delete_attachment', array( $this, 'delete_attachment' ) );
75
+ add_filter( 'wp_insert_attachment_data', array( $this, 'check_inserting_new_attachment' ), 10, 2 );
76
+ add_filter( 'update_attached_file', array( $this, 'update_attached_file' ) );
77
+ add_filter( 'wp_update_attachment_metadata', array( $this, 'update_attachment_metadata' ) );
 
 
 
 
 
 
 
78
  }
79
 
80
+ add_filter( 'w3tc_preflush_cdn_all', array( $this, 'w3tc_preflush_cdn_all' ), 10, 2 );
81
+ add_filter( 'w3tc_admin_bar_menu', array( $this, 'w3tc_admin_bar_menu' ) );
 
 
 
82
 
83
  if ( is_admin() ) {
84
+ add_action( 'w3tc_config_ui_save-w3tc_cdn', array( $this, 'change_canonical_header' ), 0, 0 );
 
85
  add_filter( 'w3tc_module_is_running-cdn', array( $this, 'cdn_is_running' ) );
86
  }
87
 
88
+ if ( ! is_admin() || $this->_config->get_boolean( 'cdn.admin.media_library' ) ) {
89
+ add_filter( 'wp_prepare_attachment_for_js', array( $this, 'wp_prepare_attachment_for_js' ), 0 );
 
 
 
 
90
  }
91
 
92
+ // Start rewrite engine.
 
 
93
  if ( $this->can_cdn() ) {
94
  Util_Bus::add_ob_callback( 'cdn', array( $this, 'ob_callback' ) );
95
  }
96
 
97
  if ( is_admin() && Cdn_Util::can_purge( $cdn_engine ) ) {
98
+ add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 0, 2 );
 
 
 
99
  }
100
 
101
+ add_filter( 'w3tc_minify_http2_preload_url', array( $this, 'w3tc_minify_http2_preload_url' ), 3000 );
 
102
  }
103
 
104
  /**
106
  *
107
  * @return Cdn_Core_Admin
108
  */
109
+ public function get_admin() {
110
  return Dispatcher::component( 'Cdn_Core_Admin' );
111
  }
112
 
113
  /**
114
  * Cron queue process event
115
  */
116
+ public function cron_queue_process() {
117
  $queue_limit = $this->_config->get_integer( 'cdn.queue.limit' );
118
  return $this->get_admin()->queue_process( $queue_limit );
119
  }
121
  /**
122
  * Cron upload event
123
  */
124
+ public function cron_upload() {
125
  $files = $this->get_files();
126
 
127
+ $upload = array();
128
  $results = array();
129
 
130
  $common = Dispatcher::component( 'Cdn_Core' );
131
 
132
  foreach ( $files as $file ) {
133
+ $local_path = $common->docroot_filename_to_absolute_path( $file );
134
  $remote_path = $common->uri_to_cdn_uri( $common->docroot_filename_to_uri( $file ) );
135
+ $upload[] = $common->build_file_descriptor( $local_path, $remote_path );
136
  }
137
 
138
  $common->upload( $upload, true, $results );
139
  }
140
 
141
+ /**
142
+ * Check attachement insert
143
+ *
144
+ * @param array $data Attachement processed data.
145
+ * @param array $postarr Attachement un-processed data.
146
+ *
147
+ * @return array
148
+ */
149
+ public function check_inserting_new_attachment( $data, $postarr ) {
150
  $this->_attachments_action[ $postarr['file'] ] = empty( $postarr['ID'] ) ? 'insert' : 'update';
151
 
152
  return $data;
153
  }
154
 
155
+ /**
156
+ * Preflush CDN.
157
+ *
158
+ * @param bool $do_flush Flush flag.
159
+ * @param array $extras Extras.
160
+ *
161
+ * @return bool
162
+ */
163
  public function w3tc_preflush_cdn_all( $do_flush, $extras = array() ) {
164
+ $default_override = Cdn_Util::get_flush_manually_default_override( $this->_config->get_string( 'cdn.engine' ) );
165
+ if ( $this->_config->get_boolean( 'cdn.flush_manually', $default_override ) ) {
166
+ if ( ! isset( $extras['ui_action'] ) ) {
167
  $do_flush = false;
168
  }
169
  }
176
  *
177
  * Upload _wp_attached_file
178
  *
179
+ * @param string $attached_file Attchement file path.
180
+ *
181
  * @return string
182
  */
183
+ public function update_attached_file( $attached_file ) {
184
  $common = Dispatcher::component( 'Cdn_Core' );
185
+ $files = $common->get_files_for_upload( $attached_file );
186
+ $files = apply_filters( 'w3tc_cdn_update_attachment', $files );
187
 
188
  $results = array();
189
 
190
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
191
  if ( Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
192
+ if ( ! array_key_exists( $attached_file, $this->_attachments_action ) || 'update' === $this->_attachments_action[ $attached_file ] ) {
193
  $common->purge( $files, $results );
194
  }
195
  } else {
204
  *
205
  * Delete _wp_attached_file, _wp_attachment_metadata, _wp_attachment_backup_sizes
206
  *
207
+ * @param integer $attachment_id Attchement ID.
208
  */
209
+ public function delete_attachment( $attachment_id ) {
210
  $common = Dispatcher::component( 'Cdn_Core' );
211
+ $files = $common->get_attachment_files( $attachment_id );
212
+ $files = apply_filters( 'w3tc_cdn_delete_attachment', $files );
213
 
214
  $results = array();
215
 
226
  *
227
  * Upload _wp_attachment_metadata
228
  *
229
+ * @param array $metadata Metadata.
230
+ *
231
  * @return array
232
  */
233
+ public function update_attachment_metadata( $metadata ) {
234
  $common = Dispatcher::component( 'Cdn_Core' );
235
+ $files = $common->get_metadata_files( $metadata );
236
+ $files = apply_filters( 'w3tc_cdn_update_attachment_metadata', $files );
237
 
238
  $results = array();
239
 
240
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
241
  if ( Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
242
+ if ( $this->_config->get_boolean( 'cdn.uploads.enable' ) ) {
243
  $common->purge( $files, $results );
244
  }
245
  } else {
252
  /**
253
  * Cron schedules filter
254
  *
255
+ * @param array $schedules Schedules.
256
+ *
257
  * @return array
258
  */
259
+ public function cron_schedules( $schedules ) {
260
  $c = $this->_config;
261
 
262
+ if ( $c->get_boolean( 'cdn.enabled' ) && ! Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
 
263
  $queue_interval = $c->get_integer( 'cdn.queue.interval' );
264
+
265
  $schedules['w3_cdn_cron_queue_process'] = array(
266
+ 'interval' => $queue_interval,
267
+ 'display' => sprintf(
268
+ // translators: 1 queue interval value.
269
+ __(
270
+ '[W3TC] CDN queue process (every %1$d seconds)',
271
+ 'w3-total-cache'
272
+ ),
273
+ $queue_interval
274
+ ),
275
+ );
276
  }
277
 
278
  if ( $c->get_boolean( 'cdn.enabled' ) &&
279
  $c->get_boolean( 'cdn.autoupload.enabled' ) &&
280
+ ! Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
281
  $autoupload_interval = $c->get_integer( 'cdn.autoupload.interval' );
282
+
283
  $schedules['w3_cdn_cron_upload'] = array(
284
+ 'interval' => $autoupload_interval,
285
+ 'display' => sprintf(
286
+ // translators: 1 queue interval value.
287
+ __(
288
+ '[W3TC] CDN auto upload (every %1$d seconds)',
289
+ 'w3-total-cache'
290
+ ),
291
+ $autoupload_interval
292
+ ),
293
+ );
294
  }
295
 
296
  return $schedules;
297
  }
298
 
299
  /**
300
+ * Switch theme action.
301
  */
302
+ public function switch_theme() {
303
  $state = Dispatcher::config_state();
304
  $state->set( 'cdn.show_note_theme_changed', true );
305
  $state->save();
306
  }
307
 
308
  /**
309
+ * WP Upgrade action hack.
310
  *
311
+ * @param string $message Message.
312
  */
313
+ public function update_feedback( $message ) {
314
+ if ( 'Upgrading database' === $message ) {
315
  $state = Dispatcher::config_state();
316
  $state->set( 'cdn.show_note_wp_upgraded', true );
317
  $state->save();
319
  }
320
 
321
  /**
322
+ * OB Callback.
323
+ *
324
+ * @param string $buffer Buffer.
325
  *
 
326
  * @return string
327
  */
328
+ public function ob_callback( $buffer ) {
329
+ if ( '' !== $buffer && Util_Content::is_html_xml( $buffer ) ) {
330
  if ( $this->can_cdn2( $buffer ) ) {
331
  $srcset_helper = new _Cdn_Plugin_ContentFilter();
332
+ $buffer = $srcset_helper->replace_all_links( $buffer );
333
 
334
  if ( $this->_debug ) {
335
  $replaced_urls = $srcset_helper->get_replaced_urls();
336
+ $buffer = $this->w3tc_footer_comment_after( $buffer, $replaced_urls );
 
337
  }
338
  }
339
  }
342
  }
343
 
344
  /**
345
+ * Returns array of files to upload.
346
  *
347
  * @return array
348
  */
349
+ public function get_files() {
350
  $files = array();
351
 
352
  if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
373
  *
374
  * @return array
375
  */
376
+ public function get_files_includes() {
377
  $includes_root = Util_Environment::normalize_path( ABSPATH . WPINC );
378
+ $doc_root = Util_Environment::normalize_path( Util_Environment::document_root() );
379
  $includes_path = ltrim( str_replace( $doc_root, '', $includes_root ), '/' );
380
 
381
  $files = Cdn_Util::search_files(
382
+ $includes_root,
383
+ $includes_path,
384
+ $this->_config->get_string( 'cdn.includes.files' )
385
  );
386
 
387
  return $files;
392
  *
393
  * @return array
394
  */
395
+ public function get_files_theme() {
396
+ // If mobile or referrer support enabled we should upload whole themes directory.
397
+ if ( $this->_config->get_boolean( 'mobile.enabled' ) || $this->_config->get_boolean( 'referrer.enabled' ) ) {
 
 
 
 
398
  $themes_root = get_theme_root();
399
  } else {
400
  $themes_root = get_stylesheet_directory();
401
  }
402
 
403
  $themes_root = Util_Environment::normalize_path( $themes_root );
404
+ $themes_path = ltrim( str_replace( Util_Environment::normalize_path( Util_Environment::document_root() ), '', $themes_root ), '/' );
405
+ $files = Cdn_Util::search_files(
406
+ $themes_root,
407
+ $themes_path,
408
+ $this->_config->get_string( 'cdn.theme.files' )
409
  );
410
 
411
  return $files;
412
  }
413
 
414
  /**
415
+ * Exports min files to CDN.
416
  *
417
  * @return array
418
  */
419
+ public function get_files_minify() {
420
  $files = array();
421
 
422
  if ( $this->_config->get_boolean( 'minify.rewrite' ) &&
423
  Util_Rule::can_check_rules() &&
424
+ (
425
+ ! $this->_config->get_boolean( 'minify.auto' ) ||
426
+ Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) )
427
+ ) ) {
428
 
429
  $minify = Dispatcher::component( 'Minify_Plugin' );
430
 
431
+ $document_root = Util_Environment::normalize_path( Util_Environment::document_root() );
432
+ $minify_root = Util_Environment::normalize_path( Util_Environment::cache_blog_dir( 'minify' ) );
433
+ $minify_path = ltrim( str_replace( $document_root, '', $minify_root ), '/' );
434
+
 
435
  $urls = $minify->get_urls();
436
 
437
+ // In WPMU + network admin (this code used for minify manual only)
438
  // common minify files are stored under context of main blog (i.e. 1)
439
+ // but have urls of 0 blog, so download has to be used.
440
+ if ( 'file' === $this->_config->get_string( 'minify.engine' ) && ! ( Util_Environment::is_wpmu() && is_network_admin() ) ) {
 
 
441
  foreach ( $urls as $url ) {
442
  Util_Http::get( $url );
443
  }
444
 
445
+ $files = Cdn_Util::search_files(
446
+ $minify_root,
447
+ $minify_path,
448
+ '*.css;*.js'
449
+ );
450
  } else {
451
  foreach ( $urls as $url ) {
452
  $file = Util_Environment::normalize_file_minify( $url );
453
 
454
+ if ( ! Util_Environment::is_url( $file ) ) {
455
  $file = $document_root . '/' . $file;
456
  $file = ltrim( str_replace( $minify_root, '', $file ), '/' );
457
 
477
  *
478
  * @return array
479
  */
480
+ public function get_files_custom() {
481
+ $files = array();
482
+ $document_root = Util_Environment::normalize_path( Util_Environment::document_root() );
483
+ $custom_files = $this->_config->get_array( 'cdn.custom.files' );
484
+ $custom_files = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $custom_files );
485
+ $site_root = Util_Environment::normalize_path( Util_Environment::site_root() );
486
+ $path = Util_Environment::site_url_uri();
 
487
  $site_root_dir = str_replace( $document_root, '', $site_root );
488
  if ( strstr( WP_CONTENT_DIR, Util_Environment::site_root() ) === false ) {
489
  $site_root = Util_Environment::normalize_path( Util_Environment::document_root() );
490
+ $path = '';
491
  }
492
 
493
  $content_path = trim( str_replace( WP_CONTENT_DIR, '', $site_root ), '/\\' );
494
 
495
  foreach ( $custom_files as $custom_file ) {
496
+ if ( '' !== $custom_file ) {
497
  $custom_file = Cdn_Util::replace_folder_placeholders( $custom_file );
498
  $custom_file = Util_Environment::normalize_file( $custom_file );
499
 
500
+ $dir = trim( dirname( $custom_file ), '/\\' );
501
+ $rel_path = $dir;
 
 
 
502
 
503
+ if ( strpos( $dir, '<currentblog>' ) !== false ) {
504
+ $dir = str_replace( '<currentblog>', 'blogs.dir/' . Util_Environment::blog_id(), $dir );
505
+ $rel_path = $dir;
 
 
506
  }
507
 
508
+ if ( '.' === $dir ) {
509
+ $dir = '';
510
+ $rel_path = $dir;
511
  }
512
+
513
+ $mask = basename( $custom_file );
514
  $files = array_merge(
515
+ $files,
516
+ Cdn_Util::search_files(
517
+ $document_root . '/' . $dir,
518
+ $rel_path,
519
+ $mask
520
+ )
521
  );
522
  }
523
  }
530
  *
531
  * @return boolean
532
  */
533
+ public function can_cdn() {
534
+ // Skip if admin.
 
 
535
  if ( defined( 'WP_ADMIN' ) ) {
536
+ $this->cdn_reject_reason = esc_html__( 'wp-admin', 'w3-total-cache' );
537
 
538
  return false;
539
  }
540
 
541
+ // Check for WPMU's and WP's 3.0 short init.
 
 
542
  if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
543
+ $this->cdn_reject_reason = esc_html__( 'Short init', 'w3-total-cache' );
544
 
545
  return false;
546
  }
547
 
548
+ // Check User agent.
549
+ if ( ! $this->check_ua() ) {
550
+ $this->cdn_reject_reason = esc_html__( 'user agent is rejected', 'w3-total-cache' );
 
 
551
 
552
  return false;
553
  }
554
 
555
+ // Check request URI.
556
+ if ( ! $this->_check_request_uri() ) {
557
+ $this->cdn_reject_reason = esc_html__( 'request URI is rejected', 'w3-total-cache' );
 
 
558
 
559
  return false;
560
  }
561
 
562
+ // Do not replace urls if SSL and SSL support is do not replace.
 
 
563
  if ( Util_Environment::is_https() && $this->_config->get_boolean( 'cdn.reject.ssl' ) ) {
564
+ $this->cdn_reject_reason = esc_html__( 'SSL is rejected', 'w3-total-cache' );
565
 
566
  return false;
567
  }
572
  /**
573
  * Returns true if we can do CDN logic
574
  *
575
+ * @param unknown $buffer Buffer.
576
+ *
577
  * @return string
578
  */
579
+ public function can_cdn2( $buffer ) {
580
+ // Check for DONOTCDN constant.
 
 
581
  if ( defined( 'DONOTCDN' ) && DONOTCDN ) {
582
+ $this->cdn_reject_reason = esc_html__( 'DONOTCDN constant is defined', 'w3-total-cache' );
583
 
584
  return false;
585
  }
586
 
587
+ // Check logged users roles.
588
+ if ( $this->_config->get_boolean( 'cdn.reject.logged_roles' ) && ! $this->_check_logged_in_role_allowed() ) {
589
+ $this->cdn_reject_reason = esc_html__( 'logged in role is rejected', 'w3-total-cache' );
 
 
 
 
590
 
591
  return false;
592
  }
595
  }
596
 
597
  /**
598
+ * Checks User Agent.
599
  *
600
  * @return boolean
601
  */
602
+ public function check_ua() {
603
+ $uas = array_merge(
604
+ $this->_config->get_array( 'cdn.reject.ua' ),
605
+ array( W3TC_POWERED_BY )
606
+ );
607
 
608
  foreach ( $uas as $ua ) {
609
+ if ( ! empty( $ua ) ) {
610
  if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ), $ua ) !== false ) {
611
  return false;
612
  }
617
  }
618
 
619
  /**
620
+ * Checks request URI.
621
  *
622
  * @return boolean
623
  */
624
+ public function _check_request_uri() {
625
  $reject_uri = $this->_config->get_array( 'cdn.reject.uri' );
626
  $reject_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $reject_uri );
627
 
629
  $expr = trim( $expr );
630
  $expr = str_replace( '~', '\~', $expr );
631
 
632
+ if ( '' !== $expr && preg_match( '~' . $expr . '~i', isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' ) ) {
633
  return false;
634
  }
635
  }
636
 
637
+ if ( Util_Request::get_string( 'wp_customize' ) ) {
 
638
  return false;
639
+ }
640
 
641
  return true;
642
  }
648
  private function _check_logged_in_role_allowed() {
649
  $current_user = wp_get_current_user();
650
 
651
+ if ( ! is_user_logged_in() ) {
652
  return true;
653
+ }
654
 
655
  $roles = $this->_config->get_array( 'cdn.reject.roles' );
656
 
657
+ if ( empty( $roles ) || empty( $current_user->roles ) || ! is_array( $current_user->roles ) ) {
 
658
  return true;
659
+ }
660
 
661
  foreach ( $current_user->roles as $role ) {
662
+ if ( in_array( $role, $roles, true ) ) {
663
  return false;
664
+ }
665
  }
666
 
667
  return true;
668
  }
669
 
670
  /**
671
+ * Media row actions filter.
672
+ *
673
+ * @param array $actions Actions.
674
+ * @param object $post Post.
675
  *
 
 
676
  * @return array
677
  */
678
+ public function media_row_actions( $actions, $post ) {
679
  return $this->get_admin()->media_row_actions( $actions, $post );
680
  }
681
 
 
682
  /**
683
+ * Get CDN running status.
684
  *
685
+ * @param unknown $current_state Current state.
686
  *
 
687
  * @return bool
688
  */
689
+ public function cdn_is_running( $current_state ) {
690
  $admin = $this->get_admin();
691
  return $admin->is_running();
692
  }
694
  /**
695
  * Change canonical header
696
  */
697
+ public function change_canonical_header() {
698
  $admin = $this->get_admin();
699
  $admin->change_canonical_header();
700
  }
701
 
702
  /**
703
+ * Adjusts attachment urls to cdn. This is for those who rely on wp_prepare_attachment_for_js().
704
+ *
705
+ * @param array $response Mixed collection of data about the attachment object.
706
  *
707
+ * @return array
 
708
  */
709
  public function wp_prepare_attachment_for_js( $response ) {
710
+ $response['url'] = $this->wp_prepare_attachment_for_js_url( $response['url'] );
711
  $response['link'] = $this->wp_prepare_attachment_for_js_url( $response['link'] );
712
 
713
+ if ( ! empty( $response['sizes'] ) ) {
714
+ foreach ( $response['sizes'] as $size => &$data ) {
715
  $data['url'] = $this->wp_prepare_attachment_for_js_url( $data['url'] );
716
  }
717
  }
720
  }
721
 
722
  /**
723
+ * An attachment's local url to modify into a cdn url.
724
  *
725
+ * @param string $url the local url to modify.
726
+ *
727
+ * @return string
728
  */
729
  private function wp_prepare_attachment_for_js_url( $url ) {
730
  $url = trim( $url );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
731
  if ( ! empty( $url ) ) {
732
+ $parsed = wp_parse_url( $url );
733
+ $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
734
+ ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
735
+
736
  $wp_upload_dir = wp_upload_dir();
737
  $upload_base_url = $wp_upload_dir['baseurl'];
738
+
739
  if ( substr( $url, 0, strlen( $upload_base_url ) ) === $upload_base_url ) {
740
  $common = Dispatcher::component( 'Cdn_Core' );
741
  $new_url = $common->url_to_cdn_url( $url, $uri );
742
  if ( ! is_null( $new_url ) ) {
743
+ $url = $new_url;
744
  }
745
  }
746
  }
747
 
748
+ return $url;
749
  }
750
 
751
  /**
752
  * Mutate http/2 header links
753
+ *
754
+ * @param array $data Data.
755
  */
756
  public function w3tc_minify_http2_preload_url( $data ) {
757
  $url = $data['result_link'];
761
  return $data;
762
  }
763
 
764
+ $parsed = wp_parse_url( $url );
765
+ $uri = ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
766
+ ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
767
 
768
+ $common = Dispatcher::component( 'Cdn_Core' );
769
  $new_url = $common->url_to_cdn_url( $url, $uri );
770
  if ( is_null( $new_url ) ) {
771
  return $data;
772
  }
773
 
774
  $data['result_link'] = $new_url;
775
+
776
+ // url_to_cdn_url processed by browsercache internally.
777
  $data['browsercache_processed'] = '*';
778
 
779
  return $data;
780
  }
781
 
782
+ /**
783
+ * Get admin bar menu items.
784
+ *
785
+ * @param array $menu_items Menu items.
786
+ */
787
  public function w3tc_admin_bar_menu( $menu_items ) {
788
  $cdn_engine = $this->_config->get_string( 'cdn.engine' );
789
 
790
  if ( Cdn_Util::can_purge_all( $cdn_engine ) ) {
791
  $menu_items['20710.cdn'] = array(
792
+ 'id' => 'w3tc_cdn_flush_all',
793
  'parent' => 'w3tc_flush',
794
+ 'title' => __( 'CDN: All', 'w3-total-cache' ),
795
+ 'href' => wp_nonce_url( admin_url( 'admin.php?page=w3tc_cdn&amp;w3tc_flush_cdn' ), 'w3tc' ),
 
 
796
  );
797
  }
798
 
799
  if ( Cdn_Util::can_purge( $cdn_engine ) ) {
800
  $menu_items['20790.cdn'] = array(
801
+ 'id' => 'w3tc_cdn_flush',
802
  'parent' => 'w3tc_flush',
803
+ 'title' => __( 'CDN: Manual Purge', 'w3-total-cache' ),
804
+ 'href' => wp_nonce_url( admin_url( 'admin.php?page=w3tc_cdn&amp;w3tc_cdn_purge' ), 'w3tc' ),
805
+ 'meta' => array( 'onclick' => 'w3tc_popupadmin_bar(this.href); return false' ),
806
  );
807
  }
808
 
809
  return $menu_items;
810
  }
811
 
812
+ /**
813
+ * Add CDN mark to footer.
814
+ *
815
+ * @param array $strings Footer strings.
816
+ */
817
  public function w3tc_footer_comment( $strings ) {
818
  $common = Dispatcher::component( 'Cdn_Core' );
819
+ $cdn = $common->get_cdn();
820
+ $via = $cdn->get_via();
821
 
822
  $strings[] = sprintf(
823
+ // translators: 1 CDN engine name, 2 rejection reason.
824
+ __(
825
+ 'Content Delivery Network via %1$s%2$s',
826
+ 'w3-total-cache'
827
+ ),
828
  ( $via ? $via : 'N/A' ),
829
+ ( empty( $this->cdn_reject_reason ) ? '' : sprintf( ' (%s)', $this->cdn_reject_reason ) )
830
+ );
831
 
832
  if ( $this->_debug ) {
833
  $strings[] = '{w3tc_cdn_debug_info}';
836
  return $strings;
837
  }
838
 
839
+ /**
840
+ * Add after footer CDN mark.
841
+ *
842
+ * @param string $buffer Buffer.
843
+ * @param array $replaced_urls Replaced URLs.
844
+ */
845
  public function w3tc_footer_comment_after( $buffer, $replaced_urls ) {
846
  $strings = array();
847
 
848
+ if ( is_array( $replaced_urls ) && count( $replaced_urls ) ) {
849
+ $strings[] = __( 'Replaced URLs for CDN:', 'w3-total-cache' );
 
850
 
851
  foreach ( $replaced_urls as $old_url => $new_url ) {
852
+ $strings[] = sprintf(
853
+ '%1$s => %2$s',
854
  Util_Content::escape_comment( $old_url ),
855
+ Util_Content::escape_comment( $new_url )
856
+ );
857
  }
858
 
859
  $strings[] = '';
860
  }
861
 
862
+ $buffer = str_replace( '{w3tc_cdn_debug_info}', implode( "\n", $strings ), $buffer );
863
+
864
  return $buffer;
865
  }
866
  }
867
 
868
+ /**
869
+ * Class: _Cdn_Plugin_ContentFilter
870
+ *
871
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
872
+ * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
873
+ */
874
+ class _Cdn_Plugin_ContentFilter { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace, PEAR.NamingConventions.ValidClassName.StartWithCapital, Generic.Files.OneObjectStructurePerFile.MultipleFound
875
+ /**
876
+ * Regular expressions.
877
+ *
878
+ * @var array
879
+ */
880
  private $_regexps = array();
881
+
882
+ /**
883
+ * Placeholders.
884
+ *
885
+ * @var array
886
+ */
887
  private $_placeholders = array();
888
+
889
+ /**
890
+ * Config.
891
+ *
892
+ * @var Config
893
+ */
894
  private $_config;
895
+
896
+ /**
897
+ * Replaced URLs.
898
+ *
899
+ * @var array
900
+ */
901
  private $_replaced_urls = array();
902
 
903
  /**
907
  */
908
  private static $_upload_scheduled = false;
909
 
910
+ /**
911
+ * Constructor.
912
+ */
913
+ public function __construct() {
914
  $this->_config = Dispatcher::config();
915
  }
916
 
917
+ /**
918
+ * Replace all links.
919
+ *
920
+ * @param string $buffer Buffer.
921
+ *
922
+ * @return string
923
+ */
924
+ public function replace_all_links( $buffer ) {
925
  $this->fill_regexps();
926
 
927
  $srcset_pattern = '~srcset\s*=\s*[\"\'](.*?)[\"\']~';
928
+ $buffer = preg_replace_callback(
929
+ $srcset_pattern,
930
+ array( $this, '_srcset_replace_callback' ),
931
+ $buffer
932
  );
933
 
934
  foreach ( $this->_regexps as $regexp ) {
935
  $buffer = preg_replace_callback(
936
+ $regexp,
937
+ array( $this, '_link_replace_callback' ),
938
+ $buffer
939
  );
940
  }
941
 
949
  $processor = array( $this, '_minify_auto_pushcdn_link_replace_callback' );
950
  }
951
  } else {
952
+ $minify_url_regexp = $this->minify_url_regexp( '/[a-z0-9]+\..+\.include(-(footer|body))?(-nb)?\.[a-f0-9]+\.(css|js)' );
953
+ $processor = array( $this, '_link_replace_callback' );
 
 
954
  }
955
 
956
+ if ( ! empty( $minify_url_regexp ) ) {
957
+ $regexp = '~(["\'(=])\s*' . $minify_url_regexp . '~U';
958
  $buffer = preg_replace_callback( $regexp, $processor, $buffer );
959
  }
960
  }
965
  }
966
 
967
  /**
968
+ * Link replace callback.
969
+ *
970
+ * @param array $matches Matches.
971
  *
 
972
  * @return string
973
  */
974
+ public function _link_replace_callback( $matches ) {
975
  list( $match, $quote, $url, , , , $path ) = $matches;
976
+
977
  $path = ltrim( $path, '/' );
978
+ $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
979
  if ( is_null( $r ) ) {
980
  $r = $this->_link_replace_callback_ask_cdn( $match, $quote, $url, $path );
981
  }
983
  return $r;
984
  }
985
 
986
+ /**
987
+ * Srcset replace callback.
988
+ *
989
+ * @param array $matches Matches.
990
+ *
991
+ * @return string
992
+ */
993
+ public function _srcset_replace_callback( $matches ) {
994
  list( $match, $srcset ) = $matches;
995
+
996
+ if ( empty( $this->_regexps ) ) {
997
  return $match;
998
+ }
999
 
1000
+ $index = '%srcset-' . count( $this->_placeholders ) . '%';
1001
+
1002
+ $srcset_urls = explode( ',', $srcset );
1003
  $new_srcset_urls = array();
1004
 
1005
  foreach ( $srcset_urls as $set ) {
1006
+ preg_match( '~(?P<spaces>^\s*)(?P<url>\S+)(?P<rest>.*)~', $set, $parts );
 
1007
  if ( isset( $parts['url'] ) ) {
 
1008
  foreach ( $this->_regexps as $regexp ) {
1009
+ $new_url = preg_replace_callback(
1010
+ $regexp,
1011
+ array(
1012
  $this,
1013
+ '_link_replace_callback',
1014
+ ),
1015
+ '"' . $parts['url'] . '">'
1016
+ );
1017
 
1018
+ if ( '"' . $parts['url'] . '">' !== $new_url ) {
1019
  $parts['url'] = substr( $new_url, 1, -2 );
1020
  break;
1021
  }
1022
  }
1023
+ $new_srcset_urls[] = $parts['spaces'] . $parts['url'] . $parts['rest'];
 
1024
  } else {
1025
  $new_srcset_urls[] = $set;
1026
  }
 
1027
  }
1028
+
1029
+ $this->_placeholders[ $index ] = implode( ',', $new_srcset_urls );
1030
+
1031
  return 'srcset="' . $index . '"';
1032
  }
1033
 
1034
+ /**
1035
+ * Replace placeholders.
1036
+ *
1037
+ * @param string $buffer Buffer.
1038
+ *
1039
+ * @return string
1040
+ */
1041
  private function replace_placeholders( $buffer ) {
1042
  foreach ( $this->_placeholders as $srcset_id => $srcset_content ) {
1043
  $buffer = str_replace( $srcset_id, $srcset_content, $buffer );
1048
  /**
1049
  * Gets regexp for minified files
1050
  *
1051
+ * @param string $filename_mask Filename mask.
1052
+ *
1053
  * @return string
1054
  */
1055
  private function minify_url_regexp( $filename_mask ) {
1056
  $minify_base_url = Util_Environment::filename_to_url(
1057
  Util_Environment::cache_blog_minify_dir()
1058
  );
1059
+ $matches = null;
1060
+ if ( ! preg_match( '~((https?://)?([^/]+))(.+)~i', $minify_base_url, $matches ) ) {
1061
+ error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
1062
+ __(
1063
+ 'Cant find minification base url, make sure minification folder sits inside WP_CONTENT_DIR and DOCUMENT_ROOT is set correctly',
1064
+ 'w3-total-cache'
1065
+ )
1066
+ );
1067
  return '';
1068
  }
1069
 
1070
  $protocol_domain_regexp = Util_Environment::get_url_regexp( $matches[1] );
1071
+ $path_regexp = Util_Environment::preg_quote( $matches[4] );
1072
 
1073
  $regexp =
1074
  '(' .
1079
  }
1080
 
1081
  /**
1082
+ * Uploads regex.
1083
  *
1084
+ * @param unknown $domain_url_regexp Domain URL regular expression.
1085
+ * @param unknown $baseurl Base URL.
1086
+ * @param unknown $upload_info Upload info.
1087
+ * @param unknown $regexps Regular expressions.
1088
  *
 
 
 
 
1089
  * @return array
1090
  */
1091
+ private function make_uploads_regexes( $domain_url_regexp, $baseurl, $upload_info, $regexps ) {
 
1092
  if ( preg_match( '~' . $domain_url_regexp . '~i', $baseurl ) ) {
1093
  $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?('
1094
  . Util_Environment::preg_quote( $upload_info['baseurlpath'] )
1095
  . '([^"\')>]+)))~i';
1096
  } else {
1097
+ $parsed = @wp_parse_url( $baseurl ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1098
+ $upload_url_domain_regexp = isset( $parsed['host'] ) ?
1099
+ Util_Environment::get_url_regexp( $parsed['scheme'] . '://' . $parsed['host'] ) :
1100
+ $domain_url_regexp;
1101
+ $baseurlpath = isset( $parsed['path'] ) ? rtrim( $parsed['path'], '/' ) : '';
1102
+ if ( $baseurlpath ) {
1103
  $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp . ')?('
1104
  . Util_Environment::preg_quote( $baseurlpath )
1105
  . '([^"\'>]+)))~i';
1106
+ } else {
1107
  $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp
1108
  . ')(([^"\'>]+)))~i';
1109
+ }
1110
  }
1111
  return $regexps;
1112
  }
1113
 
1114
+ /**
1115
+ * Fill regular expressions.
1116
+ */
1117
  private function fill_regexps() {
1118
  $regexps = array();
1119
 
1120
+ $site_path = Util_Environment::site_url_uri();
1121
  $domain_url_regexp = Util_Environment::home_domain_root_url_regexp();
1122
 
1123
  $site_domain_url_regexp = false;
1124
+ if ( Util_Environment::get_url_regexp( Util_Environment::url_to_host( site_url() ) ) !== $domain_url_regexp ) {
1125
+ $site_domain_url_regexp = Util_Environment::get_url_regexp( Util_Environment::url_to_host( site_url() ) );
1126
+ }
 
 
1127
 
1128
  if ( $this->_config->get_boolean( 'cdn.uploads.enable' ) ) {
1129
  $upload_info = Util_Http::upload_info();
1132
  $baseurl = $upload_info['baseurl'];
1133
 
1134
  if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
1135
+ $parsed = @wp_parse_url( $upload_info['baseurl'] ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
1136
  $baseurl = home_url() . $parsed['path'];
1137
  }
1138
 
1139
  $regexps = $this->make_uploads_regexes(
1140
+ $domain_url_regexp,
1141
+ $baseurl,
1142
+ $upload_info,
1143
+ $regexps
1144
  );
1145
+
1146
+ if ( $site_domain_url_regexp ) {
1147
  $regexps = $this->make_uploads_regexes(
1148
+ $site_domain_url_regexp,
1149
+ $baseurl,
1150
+ $upload_info,
1151
+ $regexps
1152
  );
1153
+ }
1154
  }
1155
  }
1156
 
1157
  if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
1158
  $mask = $this->_config->get_string( 'cdn.includes.files' );
1159
+ if ( '' !== $mask ) {
1160
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?(' .
 
1161
  Util_Environment::preg_quote( $site_path . WPINC ) .
1162
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
1163
+ if ( $site_domain_url_regexp ) {
1164
+ $regexps[] = '~(["\'(=])\s*((' . $site_domain_url_regexp . ')?(' .
 
1165
  Util_Environment::preg_quote( $site_path . WPINC ) .
1166
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
1167
  ')([^"\'() >]*)))~i';
1168
+ }
1169
  }
1170
  }
1171
 
1172
  if ( $this->_config->get_boolean( 'cdn.theme.enable' ) ) {
1173
+ $theme_dir = preg_replace( '~' . $domain_url_regexp . '~i', '', get_theme_root_uri() );
 
1174
 
1175
  $mask = $this->_config->get_string( 'cdn.theme.files' );
1176
 
1177
+ if ( '' !== $mask ) {
1178
  $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?(' .
1179
  Util_Environment::preg_quote( $theme_dir ) . '/(' .
1180
  Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
1181
  if ( $site_domain_url_regexp ) {
1182
+ $theme_dir2 = preg_replace( '~' . $site_domain_url_regexp . '~i', '', get_theme_root_uri() );
1183
+ $regexps[] = '~(["\'(=])\s*((' . $site_domain_url_regexp . ')?(' .
 
 
1184
  Util_Environment::preg_quote( $theme_dir ) . '/(' .
1185
  Cdn_Util::get_regexp_by_mask( $mask ) .
1186
  ')([^"\'() >]*)))~i';
1187
+ $regexps[] = '~(["\'(=])\s*((' . $site_domain_url_regexp . ')?(' .
 
1188
  Util_Environment::preg_quote( $theme_dir2 ) .
1189
  '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
1190
  ')([^"\'() >]*)))~i';
1198
  $masks = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $masks );
1199
 
1200
  if ( count( $masks ) ) {
1201
+ $custom_regexps_urls = array();
1202
+ $custom_regexps_uris = array();
1203
  $custom_regexps_docroot_related = array();
1204
 
1205
  foreach ( $masks as $mask ) {
1206
+ if ( ! empty( $mask ) ) {
1207
  if ( Util_Environment::is_url( $mask ) ) {
1208
  $url_match = array();
1209
  if ( preg_match( '~^((https?:)?//([^/]*))(.*)~', $mask, $url_match ) ) {
1210
  $custom_regexps_urls[] = array(
1211
+ 'domain_url' => Util_Environment::get_url_regexp( $url_match[1] ),
1212
+ 'uri' => Cdn_Util::get_regexp_by_mask( $url_match[4] ),
 
1213
  );
1214
  }
1215
+ } elseif ( '/' === substr( $mask, 0, 1 ) ) { // uri.
1216
  $custom_regexps_uris[] = Cdn_Util::get_regexp_by_mask( $mask );
1217
  } else {
1218
+ $file = Util_Environment::normalize_path( $mask ); // \ -> backspaces.
1219
  $file = str_replace( Util_Environment::site_root(), '', $file );
1220
  $file = ltrim( $file, '/' );
1221
 
1236
  }
1237
 
1238
  if ( count( $custom_regexps_docroot_related ) > 0 ) {
1239
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?(' .
1240
+ Util_Environment::preg_quote( $site_path ) .
1241
  '(' . implode( '|', $custom_regexps_docroot_related ) . ')([^"\'() >]*)))~i';
1242
+ if ( $site_domain_url_regexp ) {
1243
+ $regexps[] = '~(["\'(=])\s*((' . $site_domain_url_regexp . ')?(' .
 
1244
  Util_Environment::preg_quote( $site_path ) . '(' .
1245
  implode( '|', $custom_regexps_docroot_related ) . ')([^"\'() >]*)))~i';
1246
+ }
1247
  }
1248
  }
1249
  }
1252
  }
1253
 
1254
  /**
1255
+ * Link replace callback, basic checks step.
1256
+ *
1257
+ * @param string $match Match.
1258
+ * @param string $quote Quote.
1259
+ * @param string $url URL.
1260
+ * @param string $path Path.
1261
  *
 
 
 
 
1262
  * @return null|string
1263
  */
1264
+ public function _link_replace_callback_checks( $match, $quote, $url, $path ) {
1265
  global $wpdb;
1266
+
1267
  static $queue = null, $reject_files = null;
1268
 
1269
  /**
1270
  * Check if URL was already replaced
1271
  */
1272
+ if ( isset( $this->_replaced_urls[ $url ] ) ) {
1273
+ return $quote . $this->_replaced_urls[ $url ];
1274
  }
1275
 
1276
  /**
1277
  * Check URL for rejected files
1278
  */
1279
+ if ( null === $reject_files ) {
1280
  $reject_files = $this->_config->get_array( 'cdn.reject.files' );
1281
  }
1282
 
1283
  foreach ( $reject_files as $reject_file ) {
1284
+ if ( '' !== $reject_file ) {
1285
  $reject_file = Cdn_Util::replace_folder_placeholders( $reject_file );
 
1286
  $reject_file = Util_Environment::normalize_file( $reject_file );
1287
 
1288
+ $reject_file_regexp = '~^(' . Cdn_Util::get_regexp_by_mask( $reject_file ) . ')~i';
 
1289
 
1290
  if ( preg_match( $reject_file_regexp, $path ) ) {
1291
  return $match;
1293
  }
1294
  }
1295
 
1296
+ // Don't replace URL for files that are in the CDN queue.
1297
+ if ( null === $queue ) {
1298
+ if ( ! Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) ) {
1299
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1300
+ $queue = $wpdb->get_var(
1301
+ $wpdb->prepare(
1302
+ // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder
1303
+ 'SELECT `remote_path` FROM `%1$s` WHERE `remote_path` = \'%2$s\'',
1304
+ $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE,
1305
+ $path
1306
+ )
1307
+ );
1308
+ } else {
1309
  $queue = false;
1310
  }
1311
  }
1312
+
1313
  if ( $queue ) {
1314
  return $match;
1315
  }
1316
+
1317
  return null;
1318
  }
1319
 
1320
  /**
1321
  * Link replace callback, url replacement using cdn engine
1322
  *
1323
+ * @param string $match Matc.
1324
+ * @param string $quote Quote.
1325
+ * @param string $url URL.
1326
+ * @param string $path Path.
1327
+ *
1328
  * @return null|string
1329
  */
1330
+ public function _link_replace_callback_ask_cdn( $match, $quote, $url, $path ) {
1331
+ $common = Dispatcher::component( 'Cdn_Core' );
1332
  $new_url = $common->url_to_cdn_url( $url, $path );
1333
+ if ( ! is_null( $new_url ) ) {
1334
+ $this->_replaced_urls[ $url ] = $new_url;
1335
  return $quote . $new_url;
1336
  }
1337
 
1339
  }
1340
 
1341
  /**
1342
+ * Link replace callback for urls from minify module using auto mode and in cdn of push type.
1343
+ *
1344
+ * @param array $matches Matches.
1345
  *
 
1346
  * @return string
1347
  */
1348
+ public function _minify_auto_pushcdn_link_replace_callback( $matches ) {
1349
  static $dispatcher = null;
1350
 
1351
  list( $match, $quote, $url, , , , $path ) = $matches;
1352
+
1353
  $path = ltrim( $path, '/' );
1354
+ $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
1355
 
1356
+ // Check if we can replace that URL (for auto mode it should be uploaded).
1357
+ if ( ! Dispatcher::is_url_cdn_uploaded( $url ) ) {
 
 
1358
  Dispatcher::component( 'Cdn_Core' )->queue_upload_url( $url );
1359
+ if ( ! self::$_upload_scheduled ) {
1360
  wp_schedule_single_event( time(), 'w3_cdn_cron_queue_process' );
1361
  add_action( 'shutdown', 'wp_cron' );
1362
 
1363
  self::$_upload_scheduled = true;
1364
  }
1365
 
 
1366
  return $match;
1367
  }
1368
 
1372
  return $r;
1373
  }
1374
 
1375
+ /**
1376
+ * Get replaced URLs.
1377
+ *
1378
+ * @return array
1379
+ */
1380
+ public function get_replaced_urls() {
1381
  return $this->_replaced_urls;
1382
  }
1383
  }
Cdn_Util.php CHANGED
@@ -1,15 +1,27 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
 
 
 
4
  class Cdn_Util {
5
  /**
6
  * Check whether $engine is correct CDN engine
7
  *
8
- * @param string $engine
9
- * @return boolean
 
10
  */
11
- static public function is_engine( $engine ) {
12
- return in_array( $engine, array(
 
 
13
  'akamai',
14
  'att',
15
  'azure',
@@ -29,34 +41,61 @@ class Cdn_Util {
29
  's3_compatible',
30
  'stackpath',
31
  'stackpath2',
32
- ) );
 
 
33
  }
34
 
35
  /**
36
  * Returns true if CDN engine is mirror
37
  *
38
- * @param string $engine
 
39
  * @return bool
40
  */
41
- static public function is_engine_mirror( $engine ) {
42
- return in_array( $engine, array(
43
- 'mirror', 'maxcdn', 'cotendo', 'cf2', 'akamai',
44
- 'edgecast', 'att', 'highwinds', 'limelight', 'rackspace_cdn',
45
- 'stackpath', 'stackpath2' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
- static public function is_engine_push( $engine ) {
49
- return !self::is_engine_mirror( $engine );
 
 
 
 
 
 
 
50
  }
51
 
52
  /**
53
- * Returns true if CDN has purge all support
 
 
54
  *
55
- * @param unknown $engine
56
  * @return bool
57
  */
58
- static public function can_purge_all( $engine ) {
59
- return in_array( $engine, array(
 
 
60
  'att',
61
  'cf2',
62
  'cotendo',
@@ -66,17 +105,22 @@ class Cdn_Util {
66
  'maxcdn',
67
  'stackpath',
68
  'stackpath2',
69
- ) );
 
 
70
  }
71
 
72
  /**
73
- * Returns true if CDN engine is supporting purge
 
 
74
  *
75
- * @param string $engine
76
  * @return bool
77
  */
78
- static public function can_purge( $engine ) {
79
- return in_array( $engine, array(
 
 
80
  'akamai',
81
  'att',
82
  'azure',
@@ -93,102 +137,132 @@ class Cdn_Util {
93
  's3_compatible',
94
  'stackpath',
95
  'stackpath2',
96
- ) );
 
 
97
  }
98
 
99
  /**
100
  * Returns true if CDN supports realtime purge. That is purging on post changes, comments etc.
101
  *
102
- * @param unknown $engine
 
103
  * @return bool
104
  */
105
- static public function supports_realtime_purge( $engine ) {
106
- return !in_array( $engine, array( 'cf2' ) );
107
  }
108
 
109
  /**
110
- * Search files
 
 
 
 
 
111
  *
112
- * @param string $search_dir
113
- * @param string $base_dir
114
- * @param string $mask
115
- * @param boolean $recursive
116
  * @return array
117
  */
118
- static function search_files( $search_dir, $base_dir, $mask = '*.*', $recursive = true ) {
119
  static $stack = array();
120
- $files = array();
 
121
  $ignore = array(
122
  '.svn',
123
  '.git',
124
  '.DS_Store',
125
  'CVS',
126
  'Thumbs.db',
127
- 'desktop.ini'
128
  );
129
 
130
- $dir = @opendir( $search_dir );
131
 
132
  if ( $dir ) {
 
133
  while ( ( $entry = @readdir( $dir ) ) !== false ) {
134
- if ( $entry != '.' && $entry != '..' && !in_array( $entry, $ignore ) ) {
135
  $path = $search_dir . '/' . $entry;
136
 
137
- if ( @is_dir( $path ) && $recursive ) {
138
  array_push( $stack, $entry );
139
- $files = array_merge( $files, self::search_files(
140
- $path, $base_dir, $mask, $recursive ) );
 
 
 
 
 
 
 
141
  array_pop( $stack );
142
  } else {
143
  $regexp = '~^(' . self::get_regexp_by_mask( $mask ) . ')$~i';
144
 
145
  if ( preg_match( $regexp, $entry ) ) {
146
- $tmp = $base_dir != '' ? $base_dir . '/' : '';
147
- $tmp .= ( $p = implode( '/', $stack ) ) != '' ? $p . '/' : '';
 
148
  $files[] = $tmp . $entry;
149
  }
150
  }
151
  }
152
  }
153
 
154
- @closedir( $dir );
155
  }
156
 
157
  return $files;
158
  }
159
 
160
  /**
161
- * Returns regexp by mask
 
 
162
  *
163
- * @param string $mask
164
  * @return string
165
  */
166
- static function get_regexp_by_mask( $mask ) {
167
  $mask = trim( $mask );
168
  $mask = Util_Environment::preg_quote( $mask );
169
 
170
- $mask = str_replace( array(
 
171
  '\*',
172
  '\?',
173
- ';'
174
- ), array(
 
175
  '@ASTERISK@',
176
  '@QUESTION@',
177
- '|'
178
- ), $mask );
 
 
179
 
180
- $regexp = str_replace( array(
 
181
  '@ASTERISK@',
182
- '@QUESTION@'
183
- ), array(
 
184
  '[^\\?\\*:\\|\'"<>]*',
185
- '[^\\?\\*:\\|\'"<>]'
186
- ), $mask );
 
 
187
 
188
  return $regexp;
189
  }
190
 
191
- static function replace_folder_placeholders( $file ) {
 
 
 
 
 
 
 
192
  static $content_dir, $plugin_dir, $upload_dir;
193
  if ( empty( $content_dir ) ) {
194
  $content_dir = str_replace( Util_Environment::document_root(), '', WP_CONTENT_DIR );
@@ -212,17 +286,25 @@ class Cdn_Util {
212
  return $file;
213
  }
214
 
215
- static function replace_folder_placeholders_to_uri( $file ) {
 
 
 
 
 
 
 
216
  static $content_uri, $plugins_uri, $uploads_uri;
217
  if ( empty( $content_uri ) ) {
218
  $content_uri = Util_Environment::url_to_uri( content_url() );
219
  $plugins_uri = Util_Environment::url_to_uri( plugins_url() );
220
 
221
  $upload_dir = Util_Environment::wp_upload_dir();
222
- if ( isset( $upload_dir['baseurl'] ) )
223
  $uploads_uri = Util_Environment::url_to_uri( $upload_dir['baseurl'] );
224
- else
225
  $uploads_uri = '';
 
226
  }
227
  $file = str_replace( '{wp_content_dir}', $content_uri, $file );
228
  $file = str_replace( '{plugins_dir}', $plugins_uri, $file );
@@ -230,4 +312,18 @@ class Cdn_Util {
230
 
231
  return $file;
232
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  }
1
  <?php
2
+ /**
3
+ * File: Cdn_Util.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
+ /**
11
+ * Class: Cdn_Util
12
+ */
13
  class Cdn_Util {
14
  /**
15
  * Check whether $engine is correct CDN engine
16
  *
17
+ * @param string $engine CDN engine.
18
+ *
19
+ * @return bool
20
  */
21
+ public static function is_engine( $engine ) {
22
+ return in_array(
23
+ $engine,
24
+ array(
25
  'akamai',
26
  'att',
27
  'azure',
41
  's3_compatible',
42
  'stackpath',
43
  'stackpath2',
44
+ ),
45
+ true
46
+ );
47
  }
48
 
49
  /**
50
  * Returns true if CDN engine is mirror
51
  *
52
+ * @param string $engine CDN engine.
53
+ *
54
  * @return bool
55
  */
56
+ public static function is_engine_mirror( $engine ) {
57
+ return in_array(
58
+ $engine,
59
+ array(
60
+ 'mirror',
61
+ 'maxcdn',
62
+ 'cotendo',
63
+ 'cf2',
64
+ 'akamai',
65
+ 'edgecast',
66
+ 'att',
67
+ 'highwinds',
68
+ 'limelight',
69
+ 'rackspace_cdn',
70
+ 'stackpath',
71
+ 'stackpath2',
72
+ ),
73
+ true
74
+ );
75
  }
76
 
77
+ /**
78
+ * Returns true if CDN engine is mirror.
79
+ *
80
+ * @param string $engine CDN engine.
81
+ *
82
+ * @return bool
83
+ */
84
+ public static function is_engine_push( $engine ) {
85
+ return ! self::is_engine_mirror( $engine );
86
  }
87
 
88
  /**
89
+ * Returns true if CDN has purge all support.
90
+ *
91
+ * @param unknown $engine CDN engine.
92
  *
 
93
  * @return bool
94
  */
95
+ public static function can_purge_all( $engine ) {
96
+ return in_array(
97
+ $engine,
98
+ array(
99
  'att',
100
  'cf2',
101
  'cotendo',
105
  'maxcdn',
106
  'stackpath',
107
  'stackpath2',
108
+ ),
109
+ true
110
+ );
111
  }
112
 
113
  /**
114
+ * Returns true if CDN engine is supporting purge.
115
+ *
116
+ * @param string $engine CDN engine.
117
  *
 
118
  * @return bool
119
  */
120
+ public static function can_purge( $engine ) {
121
+ return in_array(
122
+ $engine,
123
+ array(
124
  'akamai',
125
  'att',
126
  'azure',
137
  's3_compatible',
138
  'stackpath',
139
  'stackpath2',
140
+ ),
141
+ true
142
+ );
143
  }
144
 
145
  /**
146
  * Returns true if CDN supports realtime purge. That is purging on post changes, comments etc.
147
  *
148
+ * @param unknown $engine CDN engine.
149
+ *
150
  * @return bool
151
  */
152
+ public static function supports_realtime_purge( $engine ) {
153
+ return ! in_array( $engine, array( 'cf2' ), true );
154
  }
155
 
156
  /**
157
+ * Search files.
158
+ *
159
+ * @param string $search_dir Search path.
160
+ * @param string $base_dir Base path.
161
+ * @param string $mask Mask value.
162
+ * @param boolean $recursive Recursive flag.
163
  *
 
 
 
 
164
  * @return array
165
  */
166
+ public static function search_files( $search_dir, $base_dir, $mask = '*.*', $recursive = true ) {
167
  static $stack = array();
168
+
169
+ $files = array();
170
  $ignore = array(
171
  '.svn',
172
  '.git',
173
  '.DS_Store',
174
  'CVS',
175
  'Thumbs.db',
176
+ 'desktop.ini',
177
  );
178
 
179
+ $dir = @opendir( $search_dir ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
180
 
181
  if ( $dir ) {
182
+ // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition, WordPress.PHP.NoSilencedErrors.Discouraged
183
  while ( ( $entry = @readdir( $dir ) ) !== false ) {
184
+ if ( '.' !== $entry && '..' !== $entry && ! in_array( $entry, $ignore, true ) ) {
185
  $path = $search_dir . '/' . $entry;
186
 
187
+ if ( @is_dir( $path ) && $recursive ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
188
  array_push( $stack, $entry );
189
+ $files = array_merge(
190
+ $files,
191
+ self::search_files(
192
+ $path,
193
+ $base_dir,
194
+ $mask,
195
+ $recursive
196
+ )
197
+ );
198
  array_pop( $stack );
199
  } else {
200
  $regexp = '~^(' . self::get_regexp_by_mask( $mask ) . ')$~i';
201
 
202
  if ( preg_match( $regexp, $entry ) ) {
203
+ $tmp = '' !== $base_dir ? $base_dir . '/' : '';
204
+ $p = implode( '/', $stack );
205
+ $tmp .= '' !== $p ? $p . '/' : '';
206
  $files[] = $tmp . $entry;
207
  }
208
  }
209
  }
210
  }
211
 
212
+ @closedir( $dir ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
213
  }
214
 
215
  return $files;
216
  }
217
 
218
  /**
219
+ * Returns regexp by mask.
220
+ *
221
+ * @param string $mask Mask pattern.
222
  *
 
223
  * @return string
224
  */
225
+ public static function get_regexp_by_mask( $mask ) {
226
  $mask = trim( $mask );
227
  $mask = Util_Environment::preg_quote( $mask );
228
 
229
+ $mask = str_replace(
230
+ array(
231
  '\*',
232
  '\?',
233
+ ';',
234
+ ),
235
+ array(
236
  '@ASTERISK@',
237
  '@QUESTION@',
238
+ '|',
239
+ ),
240
+ $mask
241
+ );
242
 
243
+ $regexp = str_replace(
244
+ array(
245
  '@ASTERISK@',
246
+ '@QUESTION@',
247
+ ),
248
+ array(
249
  '[^\\?\\*:\\|\'"<>]*',
250
+ '[^\\?\\*:\\|\'"<>]',
251
+ ),
252
+ $mask
253
+ );
254
 
255
  return $regexp;
256
  }
257
 
258
+ /**
259
+ * Replaces folder placeholders.
260
+ *
261
+ * @param string $file Replacement value.
262
+ *
263
+ * @return string
264
+ */
265
+ public static function replace_folder_placeholders( $file ) {
266
  static $content_dir, $plugin_dir, $upload_dir;
267
  if ( empty( $content_dir ) ) {
268
  $content_dir = str_replace( Util_Environment::document_root(), '', WP_CONTENT_DIR );
286
  return $file;
287
  }
288
 
289
+ /**
290
+ * Replaces folder placeholders URI.
291
+ *
292
+ * @param string $file Replacement value.
293
+ *
294
+ * @return string
295
+ */
296
+ public static function replace_folder_placeholders_to_uri( $file ) {
297
  static $content_uri, $plugins_uri, $uploads_uri;
298
  if ( empty( $content_uri ) ) {
299
  $content_uri = Util_Environment::url_to_uri( content_url() );
300
  $plugins_uri = Util_Environment::url_to_uri( plugins_url() );
301
 
302
  $upload_dir = Util_Environment::wp_upload_dir();
303
+ if ( isset( $upload_dir['baseurl'] ) ) {
304
  $uploads_uri = Util_Environment::url_to_uri( $upload_dir['baseurl'] );
305
+ } else {
306
  $uploads_uri = '';
307
+ }
308
  }
309
  $file = str_replace( '{wp_content_dir}', $content_uri, $file );
310
  $file = str_replace( '{plugins_dir}', $plugins_uri, $file );
312
 
313
  return $file;
314
  }
315
+
316
+ /**
317
+ * Get the override default value for cdn.flush_manually to prevent excessive invalidation charges for S3 CF and CF2.
318
+ *
319
+ * @since 2.2.6
320
+ *
321
+ * @param string $cdn_engine CDN engine value.
322
+ *
323
+ * @return boolean default value override;
324
+ */
325
+ public static function get_flush_manually_default_override( $cdn_engine = null ) {
326
+ $override_targets = array( 's3', 'cf', 'cf2' );
327
+ return in_array( $cdn_engine, $override_targets, true );
328
+ }
329
  }
Cdnfsd_CacheFlush.php CHANGED
@@ -1,128 +1,137 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
4
  /**
5
- * CDN cache flusher
6
  */
7
  class Cdnfsd_CacheFlush {
8
  /**
9
- * Array of urls to flush
10
  *
11
  * @var array
12
  */
13
  private $queued_urls = array();
14
- private $flush_all_requested = false;
15
-
16
 
 
 
 
 
 
 
17
 
18
  /**
19
- * Purges everything from CDNs that supports it
 
 
 
 
20
  */
21
- static public function w3tc_flush_all( $extras = null ) {
22
- if ( isset( $extras['only'] ) && $extras['only'] != 'cdn' )
23
  return;
 
24
 
25
- $config = Dispatcher::config();
26
- if ( $config->get_boolean( 'cdn.flush_manually' ) ) {
27
- // in this mode flush only on purge button clicks
28
- if ( !isset( $extras['ui_action'] ) ) {
 
 
29
  return true;
30
  }
31
  }
32
 
33
- $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
34
-
35
  $o->flush_all_requested = true;
 
36
  return true;
37
  }
38
 
39
-
40
-
41
  /**
42
  * Purges cdn's post cache
43
  *
44
- * @param integer $post_id
45
- * @return boolean
 
 
46
  */
47
- static public function w3tc_flush_post( $post_id, $extras = null ) {
48
- if ( !$post_id ) {
49
  $post_id = Util_Environment::detect_post_id();
50
  }
51
 
52
- if ( !$post_id )
53
  return false;
 
54
 
55
- $config = Dispatcher::config();
56
- if ( $config->get_boolean( 'cdn.flush_manually' ) ) {
57
- // in this mode flush only on purge button clicks
58
- if ( !isset( $extras['ui_action'] ) ) {
 
 
59
  return true;
60
  }
61
  }
62
 
63
- global $wp_rewrite; // required by many Util_PageUrls methods
64
  if ( empty( $wp_rewrite ) ) {
65
- error_log('Post was modified before wp_rewrite initialization. Cant flush cache.');
66
  return false;
67
  }
68
 
69
  $full_urls = array();
70
- $post = null;
71
- $terms = array();
72
 
73
- $feeds = $config->get_array( 'pgcache.purge.feed.types' );
74
  $limit_post_pages = $config->get_integer( 'pgcache.purge.postpages_limit' );
75
 
76
  if ( $config->get_boolean( 'pgcache.purge.terms' ) || $config->get_boolean( 'varnish.pgcache.feed.terms' ) ) {
77
  $taxonomies = get_post_taxonomies( $post_id );
78
- $terms = wp_get_post_terms( $post_id, $taxonomies );
79
  }
80
 
81
  switch ( true ) {
82
- case $config->get_boolean( 'pgcache.purge.author' ):
83
- case $config->get_boolean( 'pgcache.purge.archive.daily' ):
84
- case $config->get_boolean( 'pgcache.purge.archive.monthly' ):
85
- case $config->get_boolean( 'pgcache.purge.archive.yearly' ):
86
- case $config->get_boolean( 'pgcache.purge.feed.author' ):
87
- $post = get_post( $post_id );
88
  }
89
 
90
  $front_page = get_option( 'show_on_front' );
91
 
92
- /**
93
- * Home (Frontpage) URL
94
- */
95
- if ( ( $config->get_boolean( 'pgcache.purge.home' ) && $front_page == 'posts' )||
96
  $config->get_boolean( 'pgcache.purge.front_page' ) ) {
97
- $full_urls = array_merge( $full_urls,
98
- Util_PageUrls::get_frontpage_urls( $limit_post_pages ) );
99
  }
100
 
101
- /**
102
- * Home (Post page) URL
103
- */
104
- if ( $config->get_boolean( 'pgcache.purge.home' ) && $front_page != 'posts' ) {
105
- $full_urls = array_merge( $full_urls,
106
- Util_PageUrls::get_postpage_urls( $limit_post_pages ) );
107
  }
108
 
109
- /**
110
- * Post URL
111
- */
112
  if ( $config->get_boolean( 'pgcache.purge.post' ) ) {
113
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_urls( $post_id ) );
114
  }
115
 
116
- /**
117
- * Post comments URLs
118
- */
119
  if ( $config->get_boolean( 'pgcache.purge.comments' ) && function_exists( 'get_comments_pagenum_link' ) ) {
120
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_comments_urls( $post_id ) );
121
  }
122
 
123
- /**
124
- * Post author URLs
125
- */
126
  if ( $config->get_boolean( 'pgcache.purge.author' ) && $post ) {
127
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_author_urls( $post->post_author, $limit_post_pages ) );
128
  }
@@ -148,86 +157,92 @@ class Cdnfsd_CacheFlush {
148
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_monthly_archive_urls( $post, $limit_post_pages ) );
149
  }
150
 
151
- /**
152
- * Yearly archive URLs
153
- */
154
  if ( $config->get_boolean( 'pgcache.purge.archive.yearly' ) && $post ) {
155
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_yearly_archive_urls( $post, $limit_post_pages ) );
156
  }
157
 
158
- /**
159
- * Feed URLs
160
- */
161
  if ( $config->get_boolean( 'pgcache.purge.feed.blog' ) ) {
162
- $full_urls = array_merge( $full_urls,
163
- Util_PageUrls::get_feed_urls( $feeds ) );
164
  }
165
 
 
166
  if ( $config->get_boolean( 'pgcache.purge.feed.comments' ) ) {
167
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_comments_urls( $post_id, $feeds ) );
168
  }
169
 
 
170
  if ( $config->get_boolean( 'pgcache.purge.feed.author' ) && $post ) {
171
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_author_urls( $post->post_author, $feeds ) );
172
  }
173
 
 
174
  if ( $config->get_boolean( 'pgcache.purge.feed.terms' ) ) {
175
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_terms_urls( $terms, $feeds ) );
176
  }
177
 
178
- /**
179
- * Purge selected pages
180
- */
181
  if ( $config->get_array( 'pgcache.purge.pages' ) ) {
182
- $pages = $config->get_array( 'pgcache.purge.pages' );
183
- $full_urls = array_merge( $full_urls,
184
- Util_PageUrls::get_pages_urls( $pages ) );
185
  }
186
 
187
- /**
188
- * Queue flush
189
- */
190
  if ( count( $full_urls ) ) {
191
  $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
192
 
193
- foreach ( $full_urls as $url )
194
- $o->queued_urls[$url] = '*';
 
195
  }
196
 
197
  return true;
198
  }
199
 
200
  /**
201
- * Purge a single url
 
 
 
202
  *
203
- * @param unknown $url
204
  */
205
- static public function w3tc_flush_url( $url, $extras = null ) {
206
- $config = Dispatcher::config();
207
- if ( $config->get_boolean( 'cdn.flush_manually' ) ) {
208
- // in this mode flush only on purge button clicks
209
- if ( !isset( $extras['ui_action'] ) ) {
 
 
210
  return true;
211
  }
212
  }
213
 
214
- $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
215
- $o->queued_urls[$url] = '*';
216
 
217
  return true;
218
  }
219
 
220
  /**
221
  * Clears global and repeated urls
 
 
 
 
 
 
222
  */
223
- static public function w3tc_flush_execute_delayed_operations( $actions_made ) {
224
  $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
225
 
226
- // protection from incorrect w3tc upgrade operation when engine gets empty
227
- $c = Dispatcher::config();
228
  $engine = $c->get_string( 'cdnfsd.engine' );
229
- if ( empty( $engine ) )
230
  return $actions_made;
 
231
 
232
  if ( $o->flush_all_requested ) {
233
  $core = Dispatcher::component( 'Cdnfsd_Core' );
@@ -235,19 +250,19 @@ class Cdnfsd_CacheFlush {
235
  try {
236
  $engine = $core->get_engine();
237
 
238
- if ( !is_null( $engine ) ) {
239
  $engine->flush_all();
240
  $actions_made[] = array( 'module' => 'cdn' );
241
  }
242
  } catch ( \Exception $ex ) {
243
  $actions_made[] = array(
244
  'module' => 'cdn',
245
- 'error' => $ex->getMessage()
246
  );
247
  }
248
 
249
  $o->flush_all_requested = false;
250
- $o->queued_urls = array();
251
  } else {
252
  $count = count( $o->queued_urls );
253
  if ( $count > 0 ) {
@@ -258,14 +273,14 @@ class Cdnfsd_CacheFlush {
258
  try {
259
  $engine = $core->get_engine();
260
 
261
- if ( !is_null( $engine ) ) {
262
  $engine->flush_urls( $urls );
263
  $actions_made[] = array( 'module' => 'cdn' );
264
  }
265
  } catch ( \Exception $ex ) {
266
  $actions_made[] = array(
267
  'module' => 'cdn',
268
- 'error' => $ex->getMessage()
269
  );
270
  }
271
 
1
  <?php
2
+ /**
3
+ * File: Cdnfsd_CacheFlush.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
  /**
11
+ * Class: Cdnfsd_CacheFlush
12
  */
13
  class Cdnfsd_CacheFlush {
14
  /**
15
+ * Array of urls to flush.
16
  *
17
  * @var array
18
  */
19
  private $queued_urls = array();
 
 
20
 
21
+ /**
22
+ * Flush all requests flag.
23
+ *
24
+ * @var bool
25
+ */
26
+ private $flush_all_requested = false;
27
 
28
  /**
29
+ * Purges everything from CDNs that supports it.
30
+ *
31
+ * @param array $extras Extras to purge.
32
+ *
33
+ * @return bool
34
  */
35
+ public static function w3tc_flush_all( $extras = null ) {
36
+ if ( isset( $extras['only'] ) && 'cdn' !== $extras['only'] ) {
37
  return;
38
+ }
39
 
40
+ $config = Dispatcher::config();
41
+ $common = Dispatcher::component( 'Cdn_Core' );
42
+ $default_override = Cdn_Util::get_flush_manually_default_override();
43
+ if ( $config->get_boolean( 'cdn.flush_manually', $default_override ) ) {
44
+ // in this mode flush only on purge button clicks.
45
+ if ( ! isset( $extras['ui_action'] ) ) {
46
  return true;
47
  }
48
  }
49
 
50
+ $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
 
51
  $o->flush_all_requested = true;
52
+
53
  return true;
54
  }
55
 
 
 
56
  /**
57
  * Purges cdn's post cache
58
  *
59
+ * @param integer $post_id Post ID.
60
+ * @param array $extras Extras.
61
+ *
62
+ * @return bool
63
  */
64
+ public static function w3tc_flush_post( $post_id, $extras = null ) {
65
+ if ( ! $post_id ) {
66
  $post_id = Util_Environment::detect_post_id();
67
  }
68
 
69
+ if ( ! $post_id ) {
70
  return false;
71
+ }
72
 
73
+ $config = Dispatcher::config();
74
+ $common = Dispatcher::component( 'Cdn_Core' );
75
+ $default_override = Cdn_Util::get_flush_manually_default_override();
76
+ if ( $config->get_boolean( 'cdn.flush_manually', $default_override ) ) {
77
+ // in this mode flush only on purge button clicks.
78
+ if ( ! isset( $extras['ui_action'] ) ) {
79
  return true;
80
  }
81
  }
82
 
83
+ global $wp_rewrite; // required by many Util_PageUrls methods.
84
  if ( empty( $wp_rewrite ) ) {
85
+ error_log( __( 'Post was modified before wp_rewrite initialization. Cant flush cache.', 'w3-total-cache' ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
86
  return false;
87
  }
88
 
89
  $full_urls = array();
90
+ $post = null;
91
+ $terms = array();
92
 
93
+ $feeds = $config->get_array( 'pgcache.purge.feed.types' );
94
  $limit_post_pages = $config->get_integer( 'pgcache.purge.postpages_limit' );
95
 
96
  if ( $config->get_boolean( 'pgcache.purge.terms' ) || $config->get_boolean( 'varnish.pgcache.feed.terms' ) ) {
97
  $taxonomies = get_post_taxonomies( $post_id );
98
+ $terms = wp_get_post_terms( $post_id, $taxonomies );
99
  }
100
 
101
  switch ( true ) {
102
+ case $config->get_boolean( 'pgcache.purge.author' ):
103
+ case $config->get_boolean( 'pgcache.purge.archive.daily' ):
104
+ case $config->get_boolean( 'pgcache.purge.archive.monthly' ):
105
+ case $config->get_boolean( 'pgcache.purge.archive.yearly' ):
106
+ case $config->get_boolean( 'pgcache.purge.feed.author' ):
107
+ $post = get_post( $post_id );
108
  }
109
 
110
  $front_page = get_option( 'show_on_front' );
111
 
112
+ // Home (Frontpage) URL.
113
+ if ( ( $config->get_boolean( 'pgcache.purge.home' ) &&
114
+ 'posts' === $front_page ) ||
 
115
  $config->get_boolean( 'pgcache.purge.front_page' ) ) {
116
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_frontpage_urls( $limit_post_pages ) );
 
117
  }
118
 
119
+ // Home (Post page) URL.
120
+ if ( $config->get_boolean( 'pgcache.purge.home' ) && 'posts' !== $front_page ) {
121
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_postpage_urls( $limit_post_pages ) );
 
 
 
122
  }
123
 
124
+ // Post URL.
 
 
125
  if ( $config->get_boolean( 'pgcache.purge.post' ) ) {
126
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_urls( $post_id ) );
127
  }
128
 
129
+ // Post comments URLs.
 
 
130
  if ( $config->get_boolean( 'pgcache.purge.comments' ) && function_exists( 'get_comments_pagenum_link' ) ) {
131
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_comments_urls( $post_id ) );
132
  }
133
 
134
+ // Post author URLs.
 
 
135
  if ( $config->get_boolean( 'pgcache.purge.author' ) && $post ) {
136
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_author_urls( $post->post_author, $limit_post_pages ) );
137
  }
157
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_monthly_archive_urls( $post, $limit_post_pages ) );
158
  }
159
 
160
+ // Yearly archive URLs.
 
 
161
  if ( $config->get_boolean( 'pgcache.purge.archive.yearly' ) && $post ) {
162
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_yearly_archive_urls( $post, $limit_post_pages ) );
163
  }
164
 
165
+ // Feed blog URLs.
 
 
166
  if ( $config->get_boolean( 'pgcache.purge.feed.blog' ) ) {
167
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_urls( $feeds ) );
 
168
  }
169
 
170
+ // Feed comments URLs.
171
  if ( $config->get_boolean( 'pgcache.purge.feed.comments' ) ) {
172
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_comments_urls( $post_id, $feeds ) );
173
  }
174
 
175
+ // Feed autor URLs.
176
  if ( $config->get_boolean( 'pgcache.purge.feed.author' ) && $post ) {
177
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_author_urls( $post->post_author, $feeds ) );
178
  }
179
 
180
+ // Feed terms URLs.
181
  if ( $config->get_boolean( 'pgcache.purge.feed.terms' ) ) {
182
  $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_terms_urls( $terms, $feeds ) );
183
  }
184
 
185
+ // Purge selected pages.
 
 
186
  if ( $config->get_array( 'pgcache.purge.pages' ) ) {
187
+ $pages = $config->get_array( 'pgcache.purge.pages' );
188
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_pages_urls( $pages ) );
 
189
  }
190
 
191
+ // Queue flush.
 
 
192
  if ( count( $full_urls ) ) {
193
  $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
194
 
195
+ foreach ( $full_urls as $url ) {
196
+ $o->queued_urls[ $url ] = '*';
197
+ }
198
  }
199
 
200
  return true;
201
  }
202
 
203
  /**
204
+ * Purge a single url.
205
+ *
206
+ * @param unknown $url URL to purge.
207
+ * @param array $extras Extras.
208
  *
209
+ * @return bool
210
  */
211
+ public static function w3tc_flush_url( $url, $extras = null ) {
212
+ $config = Dispatcher::config();
213
+ $common = Dispatcher::component( 'Cdn_Core' );
214
+ $default_override = Cdn_Util::get_flush_manually_default_override();
215
+ if ( $config->get_boolean( 'cdn.flush_manually', $default_override ) ) {
216
+ // in this mode flush only on purge button clicks.
217
+ if ( ! isset( $extras['ui_action'] ) ) {
218
  return true;
219
  }
220
  }
221
 
222
+ $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
223
+ $o->queued_urls[ $url ] = '*';
224
 
225
  return true;
226
  }
227
 
228
  /**
229
  * Clears global and repeated urls
230
+ *
231
+ * @param array $actions_made Actions made.
232
+ *
233
+ * @throws \Exception Exception.
234
+ *
235
+ * @return array
236
  */
237
+ public static function w3tc_flush_execute_delayed_operations( $actions_made ) {
238
  $o = Dispatcher::component( 'Cdnfsd_CacheFlush' );
239
 
240
+ // Protection from incorrect w3tc upgrade operation when engine gets empty.
241
+ $c = Dispatcher::config();
242
  $engine = $c->get_string( 'cdnfsd.engine' );
243
+ if ( empty( $engine ) ) {
244
  return $actions_made;
245
+ }
246
 
247
  if ( $o->flush_all_requested ) {
248
  $core = Dispatcher::component( 'Cdnfsd_Core' );
250
  try {
251
  $engine = $core->get_engine();
252
 
253
+ if ( ! is_null( $engine ) ) {
254
  $engine->flush_all();
255
  $actions_made[] = array( 'module' => 'cdn' );
256
  }
257
  } catch ( \Exception $ex ) {
258
  $actions_made[] = array(
259
  'module' => 'cdn',
260
+ 'error' => $ex->getMessage(),
261
  );
262
  }
263
 
264
  $o->flush_all_requested = false;
265
+ $o->queued_urls = array();
266
  } else {
267
  $count = count( $o->queued_urls );
268
  if ( $count > 0 ) {
273
  try {
274
  $engine = $core->get_engine();
275
 
276
+ if ( ! is_null( $engine ) ) {
277
  $engine->flush_urls( $urls );
278
  $actions_made[] = array( 'module' => 'cdn' );
279
  }
280
  } catch ( \Exception $ex ) {
281
  $actions_made[] = array(
282
  'module' => 'cdn',
283
+ 'error' => $ex->getMessage(),
284
  );
285
  }
286
 
DbCache_WpdbInjection_QueryCaching.php CHANGED
@@ -3,14 +3,15 @@
3
  * File: DbCache_WpdbInjection_QueryCaching.php
4
  *
5
  * @package W3TC
6
- *
7
- * phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize, PSR2.Methods.MethodDeclaration.Underscore, PSR2.Classes.PropertyDeclaration.Underscore
8
  */
9
 
10
  namespace W3TC;
11
 
12
  /**
13
  * Class: DbCache_WpdbInjection_QueryCaching
 
 
 
14
  */
15
  class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
16
  /**
@@ -43,6 +44,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
43
 
44
  /**
45
  * Config.
 
 
46
  */
47
  public $_config = null;
48
 
@@ -62,7 +65,6 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
62
 
63
  /**
64
  * Request-global cache reject reason.
65
- * null until filled.
66
  *
67
  * @var string
68
  */
@@ -70,20 +72,48 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
70
 
71
  /**
72
  * Request-global check reject scope.
73
- * false until set.
74
  *
75
  * @var bool
76
  */
77
  private $cache_reject_request_wide = false;
 
 
 
 
 
 
78
  private $debug = false;
 
 
 
 
 
 
79
  private $reject_logged = false;
80
- private $reject_constants;
81
- private $use_filters;
 
 
 
 
82
  private $log_filehandle = false;
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  /**
85
  * Result of check if caching is possible at the level of current http request.
86
- * null until filled.
87
  *
88
  * @var bool
89
  */
@@ -125,15 +155,15 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
125
  $caching = $this->_can_cache( $query, $reject_reason );
126
  if ( preg_match( '~^\s*start transaction\b~is', $query ) ) {
127
  $this->cache_reject_reason = 'transaction';
128
- $reject_reason = $this->cache_reject_reason;
129
- $caching = false;
130
  }
131
 
132
  if ( preg_match( '~^\s*insert\b|^\s*delete\b|^\s*update\b|^\s*replace\b|^\s*commit\b|^\s*truncate\b|^\s*drop\b|^\s*create\b~is', $query ) ) {
133
  $this->cache_reject_reason = 'modification query';
134
- $reject_reason = $this->cache_reject_reason;
135
- $caching = false;
136
- $flush_after_query = true;
137
  }
138
 
139
  if ( $this->use_filters && function_exists( 'apply_filters' ) ) {
@@ -219,16 +249,16 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
219
  gmdate( 'r' ),
220
  strtr(
221
  isset( $_SERVER['REQUEST_URI'] ) ?
222
- esc_url_raw( stripslashes( $_SERVER['REQUEST_URI'] ) ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
223
  "<>\r\n",
224
  '.. '
225
  ),
226
- strtr( $query, "<>\r\n", '.. ' ), // 'query'.
227
- (int) ( $time_total * 1000000 ), // 'time_total' (microsecs).
228
- $reject_reason, // 'reason'.
229
- $is_cache_hit, // 'cached'.
230
- ( $data ? strlen( serialize( $data ) ) : 0 ), // 'data_size'.
231
- strtr( $group, "<>\r\n", '.. ' ), // 'group'.
232
  )
233
  );
234
  }
@@ -270,6 +300,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
270
  * @param string $table Table.
271
  * @param array $data Data.
272
  * @param array|string $format Format.
 
273
  * @return int|false
274
  */
275
  public function insert( $table, $data, $format = null ) {
@@ -282,6 +313,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
282
  * @param string $table Table.
283
  * @param array $data Data.
284
  * @param array|string $format Format.
 
285
  * @return int|false
286
  */
287
  public function replace( $table, $data, $format = null ) {
@@ -302,6 +334,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
302
  * @param array $where Where.
303
  * @param array|string $format Format.
304
  * @param array|string $where_format Format where.
 
305
  * @return int|false
306
  */
307
  public function update( $table, $data, $where, $format = null, $where_format = null ) {
@@ -316,6 +349,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
316
  * @param string $table Table.
317
  * @param array $where Where.
318
  * @param array|string $where_format Format where.
 
 
319
  */
320
  public function delete( $table, $where, $where_format = null ) {
321
  $group = $this->_get_group( $table );
@@ -327,6 +362,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
327
  * Flushes cache.
328
  *
329
  * @param array $extras Extra arguments.
 
330
  * @return bool
331
  */
332
  public function flush_cache( $extras = array() ) {
@@ -340,6 +376,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
340
  *
341
  * @param string $group Group.
342
  * @param array $extras Extra arguments.
 
343
  * @return bool
344
  */
345
  private function _flush_cache_for_sql_group( $group, $extras = array() ) {
@@ -349,7 +386,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
349
  $filename = Util_Debug::log(
350
  'dbcache',
351
  'flushing based on sqlquery group ' . $group .
352
- ' with extras ' . json_encode( $extras )
353
  );
354
  }
355
  if ( $this->_config->get_boolean( 'dbcache.debug_purge' ) ) {
@@ -397,7 +434,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
397
  'aws_autodiscovery' => $this->_config->get_boolean( 'dbcache.memcached.aws_autodiscovery' ),
398
  'username' => $this->_config->get_string( 'dbcache.memcached.username' ),
399
  'password' => $this->_config->get_string( 'dbcache.memcached.password' ),
400
- 'binary_protocol' => $this->_config->get_boolean( 'dbcache.memcached.binary_protocol' )
401
  );
402
  break;
403
 
@@ -410,7 +447,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
410
  'retry_interval' => $this->_config->get_integer( 'dbcache.redis.retry_interval' ),
411
  'read_timeout' => $this->_config->get_integer( 'dbcache.redis.read_timeout' ),
412
  'dbid' => $this->_config->get_integer( 'dbcache.redis.dbid' ),
413
- 'password' => $this->_config->get_string( 'dbcache.redis.password' )
414
  );
415
  break;
416
 
@@ -419,7 +456,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
419
  'use_wp_hash' => true,
420
  'section' => 'db',
421
  'locking' => $this->_config->get_boolean( 'dbcache.file.locking' ),
422
- 'flush_timelimit' => $this->_config->get_integer( 'timelimit.cache_flush' )
423
  );
424
  break;
425
 
@@ -441,6 +478,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
441
  *
442
  * @param string $sql SQL query.
443
  * @param string $cache_reject_reason Cache reject reason.
 
444
  * @return boolean
445
  */
446
  public function _can_cache( $sql, &$cache_reject_reason ) {
@@ -471,7 +509,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
471
  foreach ( $this->reject_constants as $name ) {
472
  if ( defined( $name ) && constant( $name ) ) {
473
  $this->cache_reject_reason = $name . ' constant defined';
474
- $cache_reject_reason = $this->cache_reject_reason;
475
 
476
  return false;
477
  }
@@ -516,7 +554,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
516
  */
517
  if ( $this->reject_logged && ! $this->_check_logged_in() ) {
518
  $this->cache_reject_reason = 'user.logged_in';
519
- $cache_reject_reason = $this->cache_reject_reason;
520
 
521
  return false;
522
  }
@@ -562,6 +600,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
562
  * Check SQL
563
  *
564
  * @param string $sql SQL query.
 
565
  * @return bool
566
  */
567
  public function _check_sql( $sql ) {
@@ -597,7 +636,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
597
  );
598
 
599
  $request_uri = isset( $_SERVER['REQUEST_URI'] ) ?
600
- esc_url_raw( stripslashes( $_SERVER['REQUEST_URI'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
601
 
602
  foreach ( $auto_reject_uri as $uri ) {
603
  if ( strstr( $request_uri, $uri ) !== false ) {
@@ -665,6 +704,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
665
  * @access private
666
  *
667
  * @param string $sql SQL query.
 
668
  * @return string
669
  */
670
  private function _get_group( $sql ) {
@@ -679,7 +719,14 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
679
 
680
  if ( $this->contains_only_tables( $tables, array( 'options' => '*' ) ) ) {
681
  $group = 'options';
682
- } elseif ( $this->contains_only_tables( $tables, array( 'comments' => '*', 'commentsmeta' => '*' ) ) ) {
 
 
 
 
 
 
 
683
  $group = 'comments';
684
  } elseif ( count( $tables ) <= 1 ) {
685
  $group = 'singletables'; // Request with single table affected.
@@ -700,7 +747,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
700
  * @accress private
701
  *
702
  * @param array $tables Tables.
703
- * #param array $allowed Allowed.
 
704
  */
705
  private function contains_only_tables( $tables, $allowed ) {
706
  if ( empty( $tables ) ) {
@@ -708,7 +756,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
708
  }
709
 
710
  foreach ( $tables as $t ) {
711
- if ( !isset( $allowed[ $t ] ) ) {
712
  return false;
713
  }
714
  }
@@ -722,6 +770,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
722
  * @access private
723
  *
724
  * @param string $group Group.
 
725
  * @param array $extras Extra arguments.
726
  */
727
  private function _get_flush_groups( $group, $extras = array() ) {
@@ -770,7 +819,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
770
  }
771
 
772
  $request_wide_string = $this->cache_reject_request_wide ?
773
- ( function_exists( '__' ) ? __( 'Request-wide', 'w3-total-cache' ).' ' : 'Request ' ) : '';
774
 
775
  return $request_wide_string . $this->_get_reject_reason_message( $this->cache_reject_reason );
776
  }
@@ -779,6 +828,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
779
  * Get reject reason message.
780
  *
781
  * @param string $key Key.
 
782
  * @return string|void
783
  */
784
  private function _get_reject_reason_message( $key ) {
@@ -820,6 +870,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
820
  * Footer comment.
821
  *
822
  * @param array $strings Strings.
 
823
  * @return array
824
  */
825
  public function w3tc_footer_comment( $strings ) {
@@ -847,10 +898,10 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
847
 
848
  if ( $this->debug ) {
849
  $strings[] = '';
850
- $strings[] = 'Db cache debug info:';
851
- $strings[] = sprintf( '%1$s%2$d', str_pad( 'Total queries: ', 20 ), $this->query_total );
852
- $strings[] = sprintf( '%1$s%2$d', str_pad( 'Cached queries: ', 20 ), $this->query_hits );
853
- $strings[] = sprintf( '%1$s%2$.4f', str_pad( 'Total query time: ', 20 ), $this->time_total );
854
  }
855
 
856
  if ( $this->log_filehandle ) {
@@ -864,13 +915,14 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
864
  * Usage statistics of request.
865
  *
866
  * @param object $storage Storage object.
 
867
  * @return void
868
  */
869
  public function w3tc_usage_statistics_of_request( $storage ) {
870
  $storage->counter_add( 'dbcache_calls_total', $this->query_total );
871
  $storage->counter_add( 'dbcache_calls_hits', $this->query_hits );
872
  $storage->counter_add( 'dbcache_flushes', $this->cache_flushes );
873
- $time_ms = (int)( $this->time_total * 1000 );
874
  $storage->counter_add( 'dbcache_time_ms', $time_ms );
875
  }
876
 
@@ -880,11 +932,12 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
880
  * @access private
881
  *
882
  * @param string $line Line to add.
 
883
  * @return void
884
  */
885
  private function log_query( $line ) {
886
  if ( ! $this->log_filehandle ) {
887
- $filename = Util_Debug::log_filename( 'dbcache-queries' );
888
  $this->log_filehandle = fopen( $filename, 'a' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
889
  }
890
 
3
  * File: DbCache_WpdbInjection_QueryCaching.php
4
  *
5
  * @package W3TC
 
 
6
  */
7
 
8
  namespace W3TC;
9
 
10
  /**
11
  * Class: DbCache_WpdbInjection_QueryCaching
12
+ *
13
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
14
+ * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
15
  */
16
  class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
17
  /**
44
 
45
  /**
46
  * Config.
47
+ *
48
+ * @var Config
49
  */
50
  public $_config = null;
51
 
65
 
66
  /**
67
  * Request-global cache reject reason.
 
68
  *
69
  * @var string
70
  */
72
 
73
  /**
74
  * Request-global check reject scope.
 
75
  *
76
  * @var bool
77
  */
78
  private $cache_reject_request_wide = false;
79
+
80
+ /**
81
+ * Debug flag.
82
+ *
83
+ * @var bool
84
+ */
85
  private $debug = false;
86
+
87
+ /**
88
+ * Reject log flag.
89
+ *
90
+ * @var bool
91
+ */
92
  private $reject_logged = false;
93
+
94
+ /**
95
+ * Log filehandle flag.
96
+ *
97
+ * @var bool
98
+ */
99
  private $log_filehandle = false;
100
 
101
+ /**
102
+ * Reject constants flag.
103
+ *
104
+ * @var bool
105
+ */
106
+ private $reject_constants = false;
107
+
108
+ /**
109
+ * Use filters flag.
110
+ *
111
+ * @var bool
112
+ */
113
+ private $use_filters = false;
114
+
115
  /**
116
  * Result of check if caching is possible at the level of current http request.
 
117
  *
118
  * @var bool
119
  */
155
  $caching = $this->_can_cache( $query, $reject_reason );
156
  if ( preg_match( '~^\s*start transaction\b~is', $query ) ) {
157
  $this->cache_reject_reason = 'transaction';
158
+ $reject_reason = $this->cache_reject_reason;
159
+ $caching = false;
160
  }
161
 
162
  if ( preg_match( '~^\s*insert\b|^\s*delete\b|^\s*update\b|^\s*replace\b|^\s*commit\b|^\s*truncate\b|^\s*drop\b|^\s*create\b~is', $query ) ) {
163
  $this->cache_reject_reason = 'modification query';
164
+ $reject_reason = $this->cache_reject_reason;
165
+ $caching = false;
166
+ $flush_after_query = true;
167
  }
168
 
169
  if ( $this->use_filters && function_exists( 'apply_filters' ) ) {
249
  gmdate( 'r' ),
250
  strtr(
251
  isset( $_SERVER['REQUEST_URI'] ) ?
252
+ filter_var( stripslashes( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
253
  "<>\r\n",
254
  '.. '
255
  ),
256
+ strtr( $query, "<>\r\n", '.. ' ), // query.
257
+ (int) ( $time_total * 1000000 ), // time_total in seconds.
258
+ $reject_reason, // reason.
259
+ $is_cache_hit, // cached.
260
+ ( $data ? strlen( serialize( $data ) ) : 0 ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- data size
261
+ strtr( $group, "<>\r\n", '.. ' ), // group.
262
  )
263
  );
264
  }
300
  * @param string $table Table.
301
  * @param array $data Data.
302
  * @param array|string $format Format.
303
+ *
304
  * @return int|false
305
  */
306
  public function insert( $table, $data, $format = null ) {
313
  * @param string $table Table.
314
  * @param array $data Data.
315
  * @param array|string $format Format.
316
+ *
317
  * @return int|false
318
  */
319
  public function replace( $table, $data, $format = null ) {
334
  * @param array $where Where.
335
  * @param array|string $format Format.
336
  * @param array|string $where_format Format where.
337
+ *
338
  * @return int|false
339
  */
340
  public function update( $table, $data, $where, $format = null, $where_format = null ) {
349
  * @param string $table Table.
350
  * @param array $where Where.
351
  * @param array|string $where_format Format where.
352
+ *
353
+ * @return int|false
354
  */
355
  public function delete( $table, $where, $where_format = null ) {
356
  $group = $this->_get_group( $table );
362
  * Flushes cache.
363
  *
364
  * @param array $extras Extra arguments.
365
+ *
366
  * @return bool
367
  */
368
  public function flush_cache( $extras = array() ) {
376
  *
377
  * @param string $group Group.
378
  * @param array $extras Extra arguments.
379
+ *
380
  * @return bool
381
  */
382
  private function _flush_cache_for_sql_group( $group, $extras = array() ) {
386
  $filename = Util_Debug::log(
387
  'dbcache',
388
  'flushing based on sqlquery group ' . $group .
389
+ ' with extras ' . wp_json_encode( $extras )
390
  );
391
  }
392
  if ( $this->_config->get_boolean( 'dbcache.debug_purge' ) ) {
434
  'aws_autodiscovery' => $this->_config->get_boolean( 'dbcache.memcached.aws_autodiscovery' ),
435
  'username' => $this->_config->get_string( 'dbcache.memcached.username' ),
436
  'password' => $this->_config->get_string( 'dbcache.memcached.password' ),
437
+ 'binary_protocol' => $this->_config->get_boolean( 'dbcache.memcached.binary_protocol' ),
438
  );
439
  break;
440
 
447
  'retry_interval' => $this->_config->get_integer( 'dbcache.redis.retry_interval' ),
448
  'read_timeout' => $this->_config->get_integer( 'dbcache.redis.read_timeout' ),
449
  'dbid' => $this->_config->get_integer( 'dbcache.redis.dbid' ),
450
+ 'password' => $this->_config->get_string( 'dbcache.redis.password' ),
451
  );
452
  break;
453
 
456
  'use_wp_hash' => true,
457
  'section' => 'db',
458
  'locking' => $this->_config->get_boolean( 'dbcache.file.locking' ),
459
+ 'flush_timelimit' => $this->_config->get_integer( 'timelimit.cache_flush' ),
460
  );
461
  break;
462
 
478
  *
479
  * @param string $sql SQL query.
480
  * @param string $cache_reject_reason Cache reject reason.
481
+ *
482
  * @return boolean
483
  */
484
  public function _can_cache( $sql, &$cache_reject_reason ) {
509
  foreach ( $this->reject_constants as $name ) {
510
  if ( defined( $name ) && constant( $name ) ) {
511
  $this->cache_reject_reason = $name . ' constant defined';
512
+ $cache_reject_reason = $this->cache_reject_reason;
513
 
514
  return false;
515
  }
554
  */
555
  if ( $this->reject_logged && ! $this->_check_logged_in() ) {
556
  $this->cache_reject_reason = 'user.logged_in';
557
+ $cache_reject_reason = $this->cache_reject_reason;
558
 
559
  return false;
560
  }
600
  * Check SQL
601
  *
602
  * @param string $sql SQL query.
603
+ *
604
  * @return bool
605
  */
606
  public function _check_sql( $sql ) {
636
  );
637
 
638
  $request_uri = isset( $_SERVER['REQUEST_URI'] ) ?
639
+ filter_var( stripslashes( $_SERVER['REQUEST_URI'] ), FILTER_SANITIZE_URL ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
640
 
641
  foreach ( $auto_reject_uri as $uri ) {
642
  if ( strstr( $request_uri, $uri ) !== false ) {
704
  * @access private
705
  *
706
  * @param string $sql SQL query.
707
+ *
708
  * @return string
709
  */
710
  private function _get_group( $sql ) {
719
 
720
  if ( $this->contains_only_tables( $tables, array( 'options' => '*' ) ) ) {
721
  $group = 'options';
722
+ } elseif (
723
+ $this->contains_only_tables(
724
+ $tables,
725
+ array(
726
+ 'comments' => '*',
727
+ 'commentsmeta' => '*',
728
+ )
729
+ ) ) {
730
  $group = 'comments';
731
  } elseif ( count( $tables ) <= 1 ) {
732
  $group = 'singletables'; // Request with single table affected.
747
  * @accress private
748
  *
749
  * @param array $tables Tables.
750
+ *
751
+ * @param array $allowed Allowed.
752
  */
753
  private function contains_only_tables( $tables, $allowed ) {
754
  if ( empty( $tables ) ) {
756
  }
757
 
758
  foreach ( $tables as $t ) {
759
+ if ( ! isset( $allowed[ $t ] ) ) {
760
  return false;
761
  }
762
  }
770
  * @access private
771
  *
772
  * @param string $group Group.
773
+ *
774
  * @param array $extras Extra arguments.
775
  */
776
  private function _get_flush_groups( $group, $extras = array() ) {
819
  }
820
 
821
  $request_wide_string = $this->cache_reject_request_wide ?
822
+ ( function_exists( '__' ) ? __( 'Request-wide ', 'w3-total-cache' ) : 'Request ' ) : '';
823
 
824
  return $request_wide_string . $this->_get_reject_reason_message( $this->cache_reject_reason );
825
  }
828
  * Get reject reason message.
829
  *
830
  * @param string $key Key.
831
+ *
832
  * @return string|void
833
  */
834
  private function _get_reject_reason_message( $key ) {
870
  * Footer comment.
871
  *
872
  * @param array $strings Strings.
873
+ *
874
  * @return array
875
  */
876
  public function w3tc_footer_comment( $strings ) {
898
 
899
  if ( $this->debug ) {
900
  $strings[] = '';
901
+ $strings[] = __( 'Db cache debug info:', 'w3-total-cache' );
902
+ $strings[] = sprintf( '%1$s%2$d', str_pad( __( 'Total queries: ', 'w3-total-cache' ), 20 ), $this->query_total );
903
+ $strings[] = sprintf( '%1$s%2$d', str_pad( __( 'Cached queries: ', 'w3-total-cache' ), 20 ), $this->query_hits );
904
+ $strings[] = sprintf( '%1$s%2$.4f', str_pad( __( 'Total query time: ', 'w3-total-cache' ), 20 ), $this->time_total );
905
  }
906
 
907
  if ( $this->log_filehandle ) {
915
  * Usage statistics of request.
916
  *
917
  * @param object $storage Storage object.
918
+ *
919
  * @return void
920
  */
921
  public function w3tc_usage_statistics_of_request( $storage ) {
922
  $storage->counter_add( 'dbcache_calls_total', $this->query_total );
923
  $storage->counter_add( 'dbcache_calls_hits', $this->query_hits );
924
  $storage->counter_add( 'dbcache_flushes', $this->cache_flushes );
925
+ $time_ms = (int) ( $this->time_total * 1000 );
926
  $storage->counter_add( 'dbcache_time_ms', $time_ms );
927
  }
928
 
932
  * @access private
933
  *
934
  * @param string $line Line to add.
935
+ *
936
  * @return void
937
  */
938
  private function log_query( $line ) {
939
  if ( ! $this->log_filehandle ) {
940
+ $filename = Util_Debug::log_filename( 'dbcache-queries' );
941
  $this->log_filehandle = fopen( $filename, 'a' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
942
  }
943
 
Generic_Plugin_Admin.php CHANGED
@@ -1,8 +1,17 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
4
  /**
5
- * class Generic_Plugin_Admin
 
 
 
6
  */
7
  class Generic_Plugin_Admin {
8
  /**
@@ -14,97 +23,64 @@ class Generic_Plugin_Admin {
14
 
15
  /**
16
  * Config
 
 
17
  */
18
  private $_config = null;
19
 
 
 
 
 
 
20
  private $is_w3tc_page;
21
- // filled with message data (see Util_Admin::redirect*)
22
- // if w3tc_message is passed
23
- private $w3tc_message = null;
24
-
25
 
 
 
 
 
 
 
26
 
27
- function __construct() {
 
 
 
28
  $this->_config = Dispatcher::config();
29
  }
30
 
31
  /**
32
  * Runs plugin
33
  */
34
- function run() {
35
  $this->is_w3tc_page = Util_Admin::is_w3tc_admin_page();
36
 
37
- add_action( 'admin_init', array(
38
- $this,
39
- 'admin_init'
40
- ) );
41
- add_action( 'admin_init_w3tc_dashboard', array(
42
- '\W3TC\Generic_WidgetServices',
43
- 'admin_init_w3tc_dashboard' ) );
44
- add_action( 'admin_init_w3tc_dashboard', array(
45
- '\W3TC\Generic_WidgetCommunity',
46
- 'admin_init_w3tc_dashboard' ) );
47
- add_action( 'admin_init_w3tc_dashboard', array(
48
- '\W3TC\Generic_WidgetBoldGrid',
49
- 'admin_init_w3tc_dashboard' ) );
50
-
51
- add_action( 'admin_enqueue_scripts', array(
52
- $this,
53
- 'admin_enqueue_scripts'
54
- ) );
55
- add_action( 'admin_print_styles-toplevel_page_w3tc_dashboard', array(
56
- '\W3TC\Generic_Page_Dashboard',
57
- 'admin_print_styles_w3tc_dashboard'
58
- ) );
59
- add_action( 'wp_ajax_w3tc_ajax', array(
60
- $this,
61
- 'wp_ajax_w3tc_ajax'
62
- ) );
63
- add_action( 'wp_ajax_w3tc_monitoring_score', array(
64
- $this,
65
- 'wp_ajax_w3tc_monitoring_score'
66
- ) );
67
-
68
- add_action( 'admin_head', array(
69
- $this,
70
- 'admin_head'
71
- ) );
72
 
73
  if ( is_network_admin() ) {
74
- add_action( 'network_admin_menu', array(
75
- $this,
76
- 'network_admin_menu'
77
- ) );
78
- add_filter( 'network_admin_plugin_action_links_' . W3TC_FILE, array(
79
- $this,
80
- 'plugin_action_links'
81
- ) );
82
  } else {
83
- add_action( 'admin_menu', array(
84
- $this,
85
- 'admin_menu'
86
- ) );
87
- add_filter( 'plugin_action_links_' . W3TC_FILE, array(
88
- $this,
89
- 'plugin_action_links'
90
- ) );
91
- }
92
-
93
- add_filter( 'favorite_actions', array(
94
- $this,
95
- 'favorite_actions'
96
- ) );
97
-
98
- add_action( 'in_plugin_update_message-' . W3TC_FILE, array(
99
- $this,
100
- 'in_plugin_update_message'
101
- ) );
102
 
103
  if ( $this->_config->get_boolean( 'pgcache.enabled' ) || $this->_config->get_boolean( 'minify.enabled' ) ) {
104
- add_filter( 'pre_update_option_active_plugins', array(
105
- $this,
106
- 'pre_update_option_active_plugins'
107
- ) );
108
  }
109
 
110
  $config_labels = new Generic_ConfigLabels();
@@ -114,27 +90,21 @@ class Generic_Plugin_Admin {
114
  add_filter( 'w3tc_notes', array( $admin_notes, 'w3tc_notes' ) );
115
  add_filter( 'w3tc_errors', array( $admin_notes, 'w3tc_errors' ), 1000 );
116
 
117
- add_action( 'w3tc_ajax_faq', array(
118
- $this,
119
- 'w3tc_ajax_faq'
120
- ) );
121
 
122
- // load w3tc_message
123
  $message_id = Util_Request::get_string( 'w3tc_message' );
124
  if ( $message_id ) {
125
  $v = get_transient( 'w3tc_message' );
126
 
127
- if ( isset( $v[$message_id] ) ) {
128
- $this->w3tc_message = $v[$message_id];
129
  delete_transient( 'w3tc_message' );
130
  }
131
  }
132
 
133
- // should be in Support_PluginAdmin, but saving loading file by being here
134
- add_action( 'admin_print_scripts-performance_page_w3tc_support', array(
135
- '\W3TC\Support_Page',
136
- 'admin_print_scripts_w3tc_support'
137
- ) );
138
  }
139
 
140
  /**
@@ -142,14 +112,14 @@ class Generic_Plugin_Admin {
142
  *
143
  * @return void
144
  */
145
- function load() {
146
  $this->add_help_tabs();
147
  $this->_page = Util_Admin::get_current_page();
148
 
149
- // run plugin action
150
  $action = false;
151
- foreach ( $_REQUEST as $key => $value ) {
152
- if ( substr( $key, 0, 5 ) == 'w3tc_' ) {
153
  $action = $key;
154
  break;
155
  }
@@ -158,31 +128,39 @@ class Generic_Plugin_Admin {
158
  $executor = new Root_AdminActions();
159
 
160
  if ( $action && $executor->exists( $action ) ) {
161
- if ( !wp_verify_nonce( Util_Request::get_string( '_wpnonce' ), 'w3tc' ) )
162
  wp_nonce_ays( 'w3tc' );
 
163
 
164
  try {
165
  $executor->execute( $action );
166
  } catch ( \Exception $e ) {
167
  $key = 'admin_action_failed_' . $action;
168
- Util_Admin::redirect_with_custom_messages( array(),
169
- array( $key => $e->getMessage() ) );
170
  }
171
 
172
  exit();
173
  }
174
  }
175
 
 
 
 
 
 
 
 
176
  public function wp_ajax_w3tc_ajax() {
177
- if ( !wp_verify_nonce( Util_Request::get_string( '_wpnonce' ), 'w3tc' ) )
178
  wp_nonce_ays( 'w3tc' );
 
179
 
180
  try {
181
  $base_capability = apply_filters( 'w3tc_ajax_base_capability_', 'manage_options' );
182
- $capability = apply_filters( 'w3tc_ajax_capability_' . Util_Request::get_string( 'w3tc_action' ),
183
- $base_capability );
184
- if ( !empty( $capability ) && !current_user_can( $capability ) )
185
  throw new \Exception( 'no permissions' );
 
186
 
187
  do_action( 'w3tc_ajax' );
188
  do_action( 'w3tc_ajax_' . Util_Request::get_string( 'w3tc_action' ) );
@@ -193,16 +171,24 @@ class Generic_Plugin_Admin {
193
  exit();
194
  }
195
 
 
 
 
 
 
 
 
196
  public function wp_ajax_w3tc_monitoring_score() {
197
- if ( !$this->_config->get_boolean( 'widget.pagespeed.show_in_admin_bar' ) )
198
  exit();
 
199
 
200
  $score = '';
201
 
202
  $modules = Dispatcher::component( 'ModuleStatus' );
203
- $score = apply_filters( 'w3tc_monitoring_score', $score );
204
 
205
- header( "Content-Type: application/x-javascript; charset=UTF-8" );
206
  echo 'document.getElementById("w3tc_monitoring_score") && ( document.getElementById("w3tc_monitoring_score").innerHTML = "' .
207
  esc_html( strtr( $score, '"', '.' ) ) . '" );';
208
 
@@ -214,9 +200,9 @@ class Generic_Plugin_Admin {
214
  *
215
  * @return void
216
  */
217
- function admin_init() {
218
- // special handling for deactivation link, it's plugins.php file
219
- if ( Util_Request::get_string( 'action' ) == 'w3tc_deactivate_plugin' ) {
220
  Util_Activation::deactivate_plugin();
221
  }
222
 
@@ -234,11 +220,11 @@ class Generic_Plugin_Admin {
234
  wp_register_style( 'w3tc-lightbox', plugins_url( 'pub/css/lightbox.css', W3TC_FILE ), array(), W3TC_VERSION );
235
  wp_register_style( 'w3tc-widget', plugins_url( 'pub/css/widget.css', W3TC_FILE ), array(), W3TC_VERSION );
236
 
237
- wp_register_script( 'w3tc-metadata', plugins_url( 'pub/js/metadata.js', W3TC_FILE ), array(), W3TC_VERSION );
238
- wp_register_script( 'w3tc-options', plugins_url( 'pub/js/options.js', W3TC_FILE ), array(), W3TC_VERSION );
239
- wp_register_script( 'w3tc-lightbox', plugins_url( 'pub/js/lightbox.js', W3TC_FILE ), array(), W3TC_VERSION );
240
- wp_register_script( 'w3tc-widget', plugins_url( 'pub/js/widget.js', W3TC_FILE ), array(), W3TC_VERSION );
241
- wp_register_script( 'w3tc-jquery-masonry', plugins_url( 'pub/js/jquery.masonry.min.js', W3TC_FILE ), array( 'jquery' ), W3TC_VERSION );
242
 
243
  // New feature count for the Feature Showcase.
244
  wp_register_script( 'w3tc-feature-counter', plugins_url( 'pub/js/feature-counter.js', W3TC_FILE ), array(), W3TC_VERSION, true );
@@ -254,21 +240,26 @@ class Generic_Plugin_Admin {
254
  wp_enqueue_script( 'w3tc-feature-counter' );
255
 
256
  // Messages.
257
- if ( !is_null( $this->w3tc_message ) &&
258
- isset( $this->w3tc_message['actions'] ) &&
259
- is_array( $this->w3tc_message['actions'] ) ) {
260
- foreach ( $this->w3tc_message['actions'] as $action )
261
  do_action( 'w3tc_message_action_' . $action );
 
262
  }
263
- // for testing.
 
264
  $w3tc_message_action_val = Util_Request::get_string( 'w3tc_message_action' );
265
  if ( ! empty( $w3tc_message_action_val ) ) {
266
  do_action( 'w3tc_message_action_' . $w3tc_message_action_val );
267
  }
268
  }
269
 
270
- // Define icon styles for the custom post type
271
- function admin_head() {
 
 
 
 
 
272
  $page = Util_Request::get_string( 'page', null );
273
 
274
  if ( ( ! is_multisite() || is_super_admin() ) && false !== strpos( $page, 'w3tc' ) && 'w3tc_setup_guide' !== $page && ! get_site_option( 'w3tc_setupguide_completed' ) ) {
@@ -276,121 +267,125 @@ class Generic_Plugin_Admin {
276
  $state_master = Dispatcher::config_state_master();
277
 
278
  if ( ! $config->get_boolean( 'pgcache.enabled' ) && $state_master->get_integer( 'common.install' ) > strtotime( 'NOW - 1 WEEK' ) ) {
279
- wp_redirect( esc_url( network_admin_url( 'admin.php?page=w3tc_setup_guide' ) ) );
280
  }
281
  }
282
 
283
  if ( 'w3tc_dashboard' === $page ) {
284
- ?>
285
  <script type="text/javascript">
286
- jQuery(function() {
287
- jQuery('#normal-sortables').masonry({
288
- itemSelector: '.postbox'
289
- });
290
- });
291
  </script>
292
  <?php
293
  }
294
 
295
  if ( $this->_config->get_boolean( 'common.track_usage' ) && $this->is_w3tc_page ) {
 
296
  $current_user = wp_get_current_user();
297
- $page = Util_Request::get_string( 'page' );
298
- if ( $page == 'w3tc_extensions' )
299
  $page = 'extensions/' . Util_Request::get_string( 'extension' );
 
300
 
301
- if ( defined( 'W3TC_DEBUG' ) && W3TC_DEBUG )
302
  $profile = 'UA-2264433-7';
303
- else
304
  $profile = 'UA-2264433-8';
 
305
 
306
  $state = Dispatcher::config_state();
307
- ?>
308
- <script type="text/javascript">
309
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
310
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
311
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
312
- })(window,document,'script','https://api.w3-edge.com/v1/analytics','w3tc_ga');
313
-
314
- if (window.w3tc_ga) {
315
- w3tc_ga('create', '<?php echo esc_html( $profile ); ?>', 'auto');
316
- w3tc_ga('set', {
317
- 'dimension1': 'w3-total-cache',
318
- 'dimension2': '<?php echo esc_html( W3TC_VERSION ); ?>',
319
- 'dimension3': '<?php global $wp_version; echo esc_html( $wp_version ); ?>',
320
- 'dimension4': 'php<?php echo esc_html( phpversion() ); ?>',
321
- 'dimension5': '<?php echo esc_attr( isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '' ); ?>',
322
- 'dimension6': 'mysql<?php global $wpdb; echo esc_attr( $wpdb->db_version() ); ?>',
323
- 'dimension7': '<?php echo esc_url( Util_Environment::home_url_host() ); ?>',
324
- 'dimension9': '<?php echo esc_attr( $state->get_string( 'common.install_version' ) ); ?>',
325
- 'dimension10': '<?php echo esc_attr( Util_Environment::w3tc_edition( $this->_config ) ); ?>',
326
- 'dimension11': '<?php echo esc_attr( Util_Widget::list_widgets() ); ?>',
327
-
328
- 'page': '<?php echo esc_attr( $page ); ?>'
329
- });
330
-
331
- w3tc_ga('send', 'pageview');
332
- }
333
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  </script>
335
  <?php
336
  }
337
 
338
- ?>
339
  <style type="text/css" media="screen">
340
- li.toplevel_page_w3tc_dashboard .wp-menu-image:before{
341
- content:'\0041';
342
- top: 2px;
343
- font-family: 'w3tc';
344
- }
345
  </style>
346
  <script>
347
- jQuery(document).ready( function($) {
348
- $('#toplevel_page_w3tc_dashboard ul li').find('a[href*="w3tc_faq"]')
349
- .prop('target','_blank')
350
- .prop('href', <?php echo json_encode(W3TC_FAQ_URL) ?>);
351
- });
352
  </script>
353
  <?php
354
  }
355
 
356
-
357
- function network_admin_menu() {
 
 
358
  $this->_admin_menu( 'manage_network_options' );
359
  }
360
 
361
- function admin_menu() {
 
 
 
362
  $this->_admin_menu( 'manage_options' );
363
  }
364
 
365
  /**
366
  * Admin menu
367
  *
 
 
368
  * @return void
369
  */
370
  private function _admin_menu( $base_capability ) {
371
- $base_capability = apply_filters( 'w3tc_capability_menu',
372
- $base_capability );
373
 
374
  if ( current_user_can( $base_capability ) ) {
375
- $menus = Dispatcher::component( 'Root_AdminMenu' );
376
  $submenu_pages = $menus->generate( $base_capability );
377
 
378
  /**
379
  * Only admin can modify W3TC settings
380
  */
381
  foreach ( $submenu_pages as $submenu_page ) {
382
- add_action( 'load-' . $submenu_page,
383
- array( $this, 'load' ) );
384
-
385
- add_action( 'admin_print_styles-' . $submenu_page,
386
- array( $this, 'admin_print_styles' ) );
387
-
388
- add_action( 'admin_print_scripts-' . $submenu_page,
389
- array( $this, 'admin_print_scripts' ) );
390
  }
391
 
392
  global $pagenow;
393
- if ( $pagenow == 'plugins.php' ) {
394
  add_action( 'admin_print_scripts', array( $this, 'load_plugins_page_js' ) );
395
  add_action( 'admin_print_styles', array( $this, 'print_plugins_page_css' ) );
396
  }
@@ -400,17 +395,10 @@ class Generic_Plugin_Admin {
400
  ! empty( Util_Request::get_string( 'w3tc_note' ) ) ||
401
  ! empty( Util_Request::get_string( 'w3tc_error' ) ) ||
402
  ! empty( Util_Request::get_string( 'w3tc_message' ) ) ) {
403
- /**
404
- * Only admin can see W3TC notices and errors
405
- */
406
- add_action( 'admin_notices', array(
407
- $this,
408
- 'admin_notices'
409
- ) );
410
- add_action( 'network_admin_notices', array(
411
- $this,
412
- 'admin_notices'
413
- ) );
414
  }
415
  }
416
  }
@@ -420,7 +408,7 @@ class Generic_Plugin_Admin {
420
  *
421
  * @return void
422
  */
423
- function admin_print_styles() {
424
  wp_enqueue_style( 'w3tc-options' );
425
  wp_enqueue_style( 'w3tc-lightbox' );
426
  }
@@ -428,7 +416,7 @@ class Generic_Plugin_Admin {
428
  /**
429
  * Print scripts.
430
  */
431
- function admin_print_scripts() {
432
  wp_enqueue_script( 'w3tc-metadata' );
433
  wp_enqueue_script( 'w3tc-options' );
434
  wp_enqueue_script( 'w3tc-lightbox' );
@@ -446,18 +434,23 @@ class Generic_Plugin_Admin {
446
  array(
447
  'cdnEnabled' => $this->_config->get_boolean( 'cdn.enabled' ),
448
  'cdnEngine' => $this->_config->get_string( 'cdn.engine' ),
449
- 'cdnFlushManually' => $this->_config->get_boolean( 'cdn.flush_manually' ),
 
 
 
450
  'cfWarning' => wp_kses(
451
  sprintf(
452
- // translators: 1: HTML break, 2: HTML anchor open tag, 3: HTML anchor close tag, 4: HTML anchor open tag.
 
453
  __(
454
- 'Please see %2$sAmazon\'s CloudFront documentation -- Paying for file invalidation%3$s:%1$sThe first 1,000 invalidation paths that you submit per month are free; you pay for each invalidation path over 1,000 in a month.%1$sYou can disable automatic purging by enabling %4$sOnly purge CDN manually%3$s.',
455
  'w3-total-cache'
456
  ),
457
- '<br />',
458
  '<a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#PayingForInvalidation">',
459
- '</a>',
460
- '<a href="' . esc_url( admin_url( 'admin.php?page=w3tc_cdn#advanced' ) ) . '">'
 
 
461
  ),
462
  array(
463
  'a' => array(
@@ -472,36 +465,46 @@ class Generic_Plugin_Admin {
472
  }
473
 
474
  switch ( $this->_page ) {
475
- case 'w3tc_minify':
476
- case 'w3tc_cachegroups':
477
- wp_enqueue_script(
478
- 'w3tc_cachegroups',
479
- plugins_url( 'CacheGroups_Plugin_Admin_View.js', W3TC_FILE ),
480
- array(
481
- 'jquery',
482
- 'jquery-ui-sortable',
483
- ),
484
- W3TC_VERSION,
485
- true
486
- );
487
- case 'w3tc_cdn':
488
- wp_enqueue_script( 'jquery-ui-sortable' );
489
- break;
 
490
  }
491
- if ( $this->_page=='w3tc_cdn' )
 
492
  wp_enqueue_script( 'jquery-ui-dialog' );
493
- if ( $this->_page=='w3tc_dashboard' )
 
 
494
  wp_enqueue_script( 'w3tc-jquery-masonry' );
 
495
  }
496
 
497
-
498
- function load_plugins_page_js() {
 
 
499
  wp_enqueue_script( 'w3tc-options' );
500
  }
501
 
502
- function print_plugins_page_css() {
503
- echo "
504
- <style type=\"text/css\">
 
 
 
505
  .w3tc-missing-files ul {
506
  margin-left: 20px;
507
  list-style-type: disc;
@@ -524,37 +527,39 @@ class Generic_Plugin_Admin {
524
  width: 170px;
525
  display: inline-block;
526
  }
527
- </style>";
 
528
  }
529
 
530
  /**
531
- * Contextual help list filter
532
- *
533
- * @param string $list
534
- * @return string
535
  */
536
- function add_help_tabs() {
537
- $screen = get_current_screen();
538
  $sections = Generic_Faq::sections();
539
- $n = 0;
540
 
541
  foreach ( $sections as $section => $data ) {
542
- $content = '<div class="w3tchelp_content" data-section="' .
543
- $section . '"></div>';
544
-
545
- $screen->add_help_tab( array(
546
- 'id' => 'w3tc_faq_' . $n,
547
- 'title' => $section,
548
- 'content' => $content
549
- ) );
 
550
  $n++;
551
  }
552
  }
553
 
 
 
 
554
  public function w3tc_ajax_faq() {
555
  $section = Util_Request::get_string( 'section' );
556
 
557
- $entries = Generic_Faq::parse( $section );
558
  $response = array();
559
 
560
  ob_start();
@@ -562,26 +567,21 @@ class Generic_Plugin_Admin {
562
  $content = ob_get_contents();
563
  ob_end_clean();
564
 
565
- echo json_encode( array( 'content' => $content ) );
566
  }
567
 
568
-
569
-
570
  /**
571
  * Plugin action links filter
572
  *
573
- * @param array $links
 
574
  * @return array
575
  */
576
- function plugin_action_links( $links ) {
577
- array_unshift( $links,
578
- '<a class="edit" href="admin.php?page=w3tc_general">Settings</a>' );
579
- array_unshift( $links,
580
- '<a class="edit" style="color: red" href="admin.php?page=w3tc_support">Premium Support</a>' );
581
-
582
 
583
- if ( !is_writable( WP_CONTENT_DIR ) ||
584
- !is_writable( Util_Rule::get_browsercache_rules_cache_path() ) ) {
585
  $delete_link = '<a href="' .
586
  wp_nonce_url( admin_url( 'plugins.php?action=w3tc_deactivate_plugin' ), 'w3tc' ) .
587
  '">Uninstall</a>';
@@ -592,15 +592,16 @@ class Generic_Plugin_Admin {
592
  }
593
 
594
  /**
595
- * favorite_actions filter
 
 
596
  *
597
- * @param array $actions
598
  * @return array
599
  */
600
- function favorite_actions( $actions ) {
601
- $actions[wp_nonce_url( admin_url( 'admin.php?page=w3tc_dashboard&amp;w3tc_flush_all' ), 'w3tc' )] = array(
602
  __( 'Empty Caches', 'w3-total-cache' ),
603
- apply_filters( 'w3tc_capability_favorite_action_flush_all', 'manage_options' )
604
  );
605
 
606
  return $actions;
@@ -609,13 +610,14 @@ class Generic_Plugin_Admin {
609
  /**
610
  * Active plugins pre update option filter
611
  *
612
- * @param string $new_value
 
613
  * @return string
614
  */
615
- function pre_update_option_active_plugins( $new_value ) {
616
  $old_value = (array) get_option( 'active_plugins' );
617
 
618
- if ( $new_value !== $old_value && in_array( W3TC_FILE, (array) $new_value ) && in_array( W3TC_FILE, (array) $old_value ) ) {
619
  $state_note = Dispatcher::config_state_note();
620
  $state_note->set( 'common.show_note.plugins_updated', true );
621
  }
@@ -628,18 +630,20 @@ class Generic_Plugin_Admin {
628
  *
629
  * @return void
630
  */
631
- function in_plugin_update_message() {
632
  $response = Util_Http::get( W3TC_README_URL );
633
 
634
- if ( is_wp_error( $response ) || $response['response']['code'] != 200 )
635
  return;
 
636
 
637
  $matches = null;
638
- $regexp = '~==\s*Changelog\s*==\s*=\s*[0-9.]+\s*=(.*)(=\s*' . preg_quote( W3TC_VERSION ) . '\s*=|$)~Uis';
639
 
640
  $body = $response['body'];
641
- if ( !preg_match( $regexp, $body, $matches ) )
642
  return;
 
643
 
644
  $changelog = (array) preg_split( '~[\r\n]+~', trim( $matches[1] ) );
645
 
@@ -648,12 +652,12 @@ class Generic_Plugin_Admin {
648
 
649
  foreach ( $changelog as $index => $line ) {
650
  if ( preg_match( '~^\s*\*\s*~', $line ) ) {
651
- if ( !$ul ) {
652
  echo '<ul style="list-style: disc; margin-left: 20px;margin-top:0;">';
653
  $ul = true;
654
  }
655
  $line = preg_replace( '~^\s*\*\s*~', '', htmlspecialchars( $line ) );
656
- echo '<li style="width: 50%; margin: 0; float: left; ' . ( $index % 2 == 0 ? 'clear: left;' : '' ) . '">' . esc_html( $line ) . '</li>';
657
  } else {
658
  if ( $ul ) {
659
  echo '</ul><div style="clear: left;"></div>';
@@ -662,8 +666,9 @@ class Generic_Plugin_Admin {
662
  }
663
  }
664
 
665
- if ( $ul )
666
  echo '</ul><div style="clear: left;"></div>';
 
667
 
668
  echo '</div>';
669
  }
@@ -673,103 +678,183 @@ class Generic_Plugin_Admin {
673
  *
674
  * @return void
675
  */
676
- function admin_notices() {
677
  $cookie_domain = Util_Admin::get_cookie_domain();
678
 
679
  $error_messages = array(
680
- 'fancy_permalinks_disabled_pgcache' => sprintf( __( 'Fancy permalinks are disabled. Please %s it first, then re-attempt to enabling enhanced disk mode.', 'w3-total-cache' ), Util_Ui::button_link( 'enable', 'options-permalink.php' ) ),
681
- 'fancy_permalinks_disabled_browsercache' => sprintf( __( 'Fancy permalinks are disabled. Please %s it first, then re-attempt to enabling the \'Do not process 404 errors for static objects with WordPress\'.', 'w3-total-cache' ), Util_Ui::button_link( 'enable', 'options-permalink.php' ) ),
682
- 'support_request' => __( 'Failed to send support request.', 'w3-total-cache' ),
683
- 'support_request_type' => __( 'Please select request type.', 'w3-total-cache' ),
684
- 'support_request_url' => __( 'Please enter the address of the site in the site <acronym title="Uniform Resource Locator">URL</acronym> field.', 'w3-total-cache' ),
685
- 'support_request_name' => __( 'Please enter your name in the Name field', 'w3-total-cache' ),
686
- 'support_request_email' => __( 'Please enter valid email address in the E-Mail field.', 'w3-total-cache' ),
687
- 'support_request_phone' => __( 'Please enter your phone in the phone field.', 'w3-total-cache' ),
688
- 'support_request_subject' => __( 'Please enter subject in the subject field.', 'w3-total-cache' ),
689
- 'support_request_description' => __( 'Please describe the issue in the issue description field.', 'w3-total-cache' ),
690
- 'support_request_wp_login' => __( 'Please enter an administrator login. Create a temporary one just for this support case if needed.', 'w3-total-cache' ),
691
- 'support_request_wp_password' => __( 'Please enter WP Admin password, be sure it\'s spelled correctly.', 'w3-total-cache' ),
692
- 'support_request_ftp_host' => __( 'Please enter <acronym title="Secure Shell">SSH</acronym> or <acronym title="File Transfer Protocol">FTP</acronym> host for the site.', 'w3-total-cache' ),
693
- 'support_request_ftp_login' => __( 'Please enter <acronym title="Secure Shell">SSH</acronym> or <acronym title="File Transfer Protocol">FTP</acronym> login for the server. Create a temporary one just for this support case if needed.', 'w3-total-cache' ),
694
- 'support_request_ftp_password' => __( 'Please enter <acronym title="Secure Shell">SSH</acronym> or <acronym title="File Transfer Protocol">FTP</acronym> password for the <acronym title="File Transfer Protocol">FTP</acronym> account.', 'w3-total-cache' ),
695
- 'support_request' => __( 'Unable to send the support request.', 'w3-total-cache' ),
696
- 'config_import_no_file' => __( 'Please select config file.', 'w3-total-cache' ),
697
- 'config_import_upload' => __( 'Unable to upload config file.', 'w3-total-cache' ),
698
- 'config_import_import' => __( 'Configuration file could not be imported.', 'w3-total-cache' ),
699
- 'config_reset' => sprintf( __( 'Default settings could not be restored. Please run <strong>chmod 777 %s</strong> to make the configuration file write-able, then try again.', 'w3-total-cache' ), W3TC_CONFIG_DIR ),
700
- 'cdn_purge_attachment' => __( 'Unable to purge attachment.', 'w3-total-cache' ),
701
- 'pgcache_purge_post' => __( 'Unable to purge post.', 'w3-total-cache' ),
702
- 'enable_cookie_domain' => sprintf( __( '<strong>%swp-config.php</strong> could not be written, please edit config and add:<br /><strong style="color:#f00;">define(\'COOKIE_DOMAIN\', \'%s\');</strong> before <strong style="color:#f00;">require_once(ABSPATH . \'wp-settings.php\');</strong>.', 'w3-total-cache' ), ABSPATH, addslashes( $cookie_domain ) ),
703
- 'disable_cookie_domain' => sprintf( __( '<strong>%swp-config.php</strong> could not be written, please edit config and add:<br /><strong style="color:#f00;">define(\'COOKIE_DOMAIN\', false);</strong> before <strong style="color:#f00;">require_once(ABSPATH . \'wp-settings.php\');</strong>.', 'w3-total-cache' ), ABSPATH ),
704
- 'pull_zone' => __( 'Pull Zone could not be automatically created.', 'w3-total-cache' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
  );
706
 
707
  $note_messages = array(
708
- 'config_save' => __( 'Plugin configuration successfully updated.', 'w3-total-cache' ),
709
- 'flush_all' => __( 'All caches successfully emptied.', 'w3-total-cache' ),
710
- 'flush_memcached' => __( 'Memcached cache(s) successfully emptied.', 'w3-total-cache' ),
711
- 'flush_opcode' => __( 'Opcode cache(s) successfully emptied.', 'w3-total-cache' ),
712
- 'flush_file' => __( 'Disk cache(s) successfully emptied.', 'w3-total-cache' ),
713
- 'flush_pgcache' => __( 'Page cache successfully emptied.', 'w3-total-cache' ),
714
- 'flush_dbcache' => __( 'Database cache successfully emptied.', 'w3-total-cache' ),
715
- 'flush_objectcache' => __( 'Object cache successfully emptied.', 'w3-total-cache' ),
716
- 'flush_fragmentcache' => __( 'Fragment cache successfully emptied.', 'w3-total-cache' ),
717
- 'flush_minify' => __( 'Minify cache successfully emptied.', 'w3-total-cache' ),
718
- 'flush_browser_cache' => __( 'Media Query string has been successfully updated.', 'w3-total-cache' ),
719
- 'flush_varnish' => __( 'Varnish servers successfully purged.', 'w3-total-cache' ),
720
- 'flush_cdn' => __( '<acronym title="Content Delivery Network">CDN</acronym> was successfully purged.', 'w3-total-cache' ),
721
- 'support_request' => __( 'The support request has been successfully sent.', 'w3-total-cache' ),
722
- 'config_import' => __( 'Settings successfully imported.', 'w3-total-cache' ),
723
- 'config_reset' => __( 'Settings successfully restored.', 'w3-total-cache' ),
724
- 'preview_enable' => __( 'Preview mode was successfully enabled', 'w3-total-cache' ),
725
- 'preview_disable' => __( 'Preview mode was successfully disabled', 'w3-total-cache' ),
726
- 'preview_deploy' => __( 'Preview settings successfully deployed. Preview mode remains enabled until it\'s disabled. Continue testing new settings or disable preview mode if done.', 'w3-total-cache' ),
 
 
 
 
 
 
 
727
  'cdn_purge_attachment' => __( 'Attachment successfully purged.', 'w3-total-cache' ),
728
- 'pgcache_purge_post' => __( 'Post successfully purged.', 'w3-total-cache' ),
729
- 'new_relic_save' => __( 'New relic settings have been updated.', 'w3-total-cache' ),
730
- 'add_in_removed' => __( 'The add-in has been removed.', 'w3-total-cache' ),
731
- 'enabled_edge' => __( 'Edge mode has been enabled.', 'w3-total-cache' ),
732
- 'disabled_edge' => __( 'Edge mode has been disabled.', 'w3-total-cache' ),
733
- 'pull_zone' => __( 'Pull Zone was automatically created.', 'w3-total-cache' )
734
  );
735
 
736
- $errors = array();
737
- $notes = array();
738
  $environment_error_present = false;
739
 
740
  $error = Util_Request::get_string( 'w3tc_error' );
741
- if ( isset( $error_messages[$error] ) )
742
- $errors[$error] = $error_messages[$error];
 
743
 
744
  $note = Util_Request::get_string( 'w3tc_note' );
745
- if ( isset( $note_messages[$note] ) )
746
- $notes[$note] = $note_messages[$note];
 
747
 
748
  // print errors happened during last request execution,
749
  // when we decided to redirect with error message instead of
750
- // printing it directly (to avoid reexecution on refresh)
751
- if ( !is_null( $this->w3tc_message ) ) {
752
  $v = $this->w3tc_message;
753
  if ( isset( $v['errors'] ) && is_array( $v['errors'] ) ) {
754
  foreach ( $v['errors'] as $error ) {
755
- if ( isset( $error_messages[$error] ) )
756
- $errors[] = $error_messages[$error];
757
- else
758
  $errors[] = $error;
 
759
  }
760
  }
761
  if ( isset( $v['notes'] ) && is_array( $v['notes'] ) ) {
762
  foreach ( $v['notes'] as $note ) {
763
- if ( isset( $note_messages[$note] ) )
764
- $notes[] = $note_messages[$note];
765
- else
766
  $notes[] = $note;
 
767
  }
768
  }
769
  }
770
 
771
  /*
772
- * Filesystem environment fix, if needed
773
  */
774
  try {
775
  $environment = Dispatcher::component( 'Root_Environment' );
@@ -783,7 +868,7 @@ class Generic_Plugin_Admin {
783
  $n = 1;
784
 
785
  foreach ( $r['before_errors'] as $e ) {
786
- $errors['generic_env_' . $n] = $e;
787
  $n++;
788
  }
789
 
@@ -792,45 +877,37 @@ class Generic_Plugin_Admin {
792
  'background: white; ' .
793
  'margin: 10px 30px 10px 30px; ' .
794
  'padding: 10px; display: none';
 
795
  $ftp_style = 'border: 1px solid black; background: white; ' .
796
  'margin: 10px 30px 10px 30px; ' .
797
  'padding: 10px; display: none';
798
- $ftp_form = str_replace( 'class="wrap"', '',
799
- $exs->credentials_form() );
800
- $ftp_form = str_replace( '<form ', '<form name="w3tc_ftp_form" ',
801
- $ftp_form );
802
  $ftp_form = str_replace( '<fieldset>', '', $ftp_form );
803
  $ftp_form = str_replace( '</fieldset>', '', $ftp_form );
804
- $ftp_form = str_replace( 'id="upgrade" class="button"',
805
- 'id="upgrade" class="button w3tc-button-save"', $ftp_form );
806
 
807
- $error = '<strong>W3 Total Cache Error:</strong> ' .
808
- 'Files and directories could not be automatically ' .
809
- 'created to complete the installation. ' .
810
  '<table>' .
811
  '<tr>' .
812
- '<td>Please execute commands manually</td>' .
813
- '<td>' .
814
- Util_Ui::button( 'View required changes', '',
815
- 'w3tc-show-required-changes' ) .
816
- '</td>' .
817
  '</tr>' .
818
  '<tr>' .
819
- '<td>or use FTP form to allow ' .
820
- '<strong>W3 Total Cache</strong> make it automatically.' .
821
- '</td>' .
822
- '<td>' .
823
- Util_Ui::button( 'Update via FTP', '', 'w3tc-show-ftp-form' ) .
824
- '</td>' .
825
- '</tr></table>'.
826
-
827
- '<div class="w3tc-required-changes" style="' .
828
- $changes_style . '">' . $r['required_changes'] . '</div>' .
829
- '<div class="w3tc-ftp-form" style="' . $ftp_style . '">' .
830
- $ftp_form . '</div>';
831
 
832
  $environment_error_present = true;
833
- $errors['generic_ftp'] = $error;
834
  }
835
 
836
  foreach ( $r['later_errors'] as $e ) {
@@ -873,7 +950,7 @@ class Generic_Plugin_Admin {
873
  printf(
874
  '<div class="error w3tc_error" id="%1$s"><p>%2$s</p></div>',
875
  esc_attr( $key ),
876
- $error // phpcs:ignore
877
  );
878
  }
879
  }
1
  <?php
2
+ /**
3
+ * File: Generic_Plugin_Admin.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
  /**
11
+ * Class Generic_Plugin_Admin
12
+ *
13
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
14
+ * phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
15
  */
16
  class Generic_Plugin_Admin {
17
  /**
23
 
24
  /**
25
  * Config
26
+ *
27
+ * @var Config
28
  */
29
  private $_config = null;
30
 
31
+ /**
32
+ * Config
33
+ *
34
+ * @var bool
35
+ */
36
  private $is_w3tc_page;
 
 
 
 
37
 
38
+ /**
39
+ * Message data (see Util_Admin::redirect*).
40
+ *
41
+ * @var array
42
+ */
43
+ private $w3tc_message = null;
44
 
45
+ /**
46
+ * Constructor.
47
+ */
48
+ public function __construct() {
49
  $this->_config = Dispatcher::config();
50
  }
51
 
52
  /**
53
  * Runs plugin
54
  */
55
+ public function run() {
56
  $this->is_w3tc_page = Util_Admin::is_w3tc_admin_page();
57
 
58
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
59
+ add_action( 'admin_init_w3tc_dashboard', array( '\W3TC\Generic_WidgetServices', 'admin_init_w3tc_dashboard' ) );
60
+ add_action( 'admin_init_w3tc_dashboard', array( '\W3TC\Generic_WidgetCommunity', 'admin_init_w3tc_dashboard' ) );
61
+ add_action( 'admin_init_w3tc_dashboard', array( '\W3TC\Generic_WidgetBoldGrid', 'admin_init_w3tc_dashboard' ) );
62
+
63
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
64
+ add_action( 'admin_print_styles-toplevel_page_w3tc_dashboard', array( '\W3TC\Generic_Page_Dashboard', 'admin_print_styles_w3tc_dashboard' ) );
65
+ add_action( 'wp_ajax_w3tc_ajax', array( $this, 'wp_ajax_w3tc_ajax' ) );
66
+ add_action( 'wp_ajax_w3tc_monitoring_score', array( $this, 'wp_ajax_w3tc_monitoring_score' ) );
67
+
68
+ add_action( 'admin_head', array( $this, 'admin_head' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  if ( is_network_admin() ) {
71
+ add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ) );
72
+ add_filter( 'network_admin_plugin_action_links_' . W3TC_FILE, array( $this, 'plugin_action_links' ) );
 
 
 
 
 
 
73
  } else {
74
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
75
+ add_filter( 'plugin_action_links_' . W3TC_FILE, array( $this, 'plugin_action_links' ) );
76
+ }
77
+
78
+ add_filter( 'favorite_actions', array( $this, 'favorite_actions' ) );
79
+
80
+ add_action( 'in_plugin_update_message-' . W3TC_FILE, array( $this, 'in_plugin_update_message' ) );
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  if ( $this->_config->get_boolean( 'pgcache.enabled' ) || $this->_config->get_boolean( 'minify.enabled' ) ) {
83
+ add_filter( 'pre_update_option_active_plugins', array( $this, 'pre_update_option_active_plugins' ) );
 
 
 
84
  }
85
 
86
  $config_labels = new Generic_ConfigLabels();
90
  add_filter( 'w3tc_notes', array( $admin_notes, 'w3tc_notes' ) );
91
  add_filter( 'w3tc_errors', array( $admin_notes, 'w3tc_errors' ), 1000 );
92
 
93
+ add_action( 'w3tc_ajax_faq', array( $this, 'w3tc_ajax_faq' ) );
 
 
 
94
 
95
+ // Load w3tc_message.
96
  $message_id = Util_Request::get_string( 'w3tc_message' );
97
  if ( $message_id ) {
98
  $v = get_transient( 'w3tc_message' );
99
 
100
+ if ( isset( $v[ $message_id ] ) ) {
101
+ $this->w3tc_message = $v[ $message_id ];
102
  delete_transient( 'w3tc_message' );
103
  }
104
  }
105
 
106
+ // Should be in Support_PluginAdmin, but saving loading file by being here.
107
+ add_action( 'admin_print_scripts-performance_page_w3tc_support', array( '\W3TC\Support_Page', 'admin_print_scripts_w3tc_support' ) );
 
 
 
108
  }
109
 
110
  /**
112
  *
113
  * @return void
114
  */
115
+ public function load() {
116
  $this->add_help_tabs();
117
  $this->_page = Util_Admin::get_current_page();
118
 
119
+ // Run plugin action.
120
  $action = false;
121
+ foreach ( $_REQUEST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
122
+ if ( 'w3tc_' === substr( $key, 0, 5 ) ) {
123
  $action = $key;
124
  break;
125
  }
128
  $executor = new Root_AdminActions();
129
 
130
  if ( $action && $executor->exists( $action ) ) {
131
+ if ( ! wp_verify_nonce( Util_Request::get_string( '_wpnonce' ), 'w3tc' ) ) {
132
  wp_nonce_ays( 'w3tc' );
133
+ }
134
 
135
  try {
136
  $executor->execute( $action );
137
  } catch ( \Exception $e ) {
138
  $key = 'admin_action_failed_' . $action;
139
+ Util_Admin::redirect_with_custom_messages( array(), array( $key => $e->getMessage() ) );
 
140
  }
141
 
142
  exit();
143
  }
144
  }
145
 
146
+ /**
147
+ * Load action
148
+ *
149
+ * @throws \Exception Exception.
150
+ *
151
+ * @return void
152
+ */
153
  public function wp_ajax_w3tc_ajax() {
154
+ if ( ! wp_verify_nonce( Util_Request::get_string( '_wpnonce' ), 'w3tc' ) ) {
155
  wp_nonce_ays( 'w3tc' );
156
+ }
157
 
158
  try {
159
  $base_capability = apply_filters( 'w3tc_ajax_base_capability_', 'manage_options' );
160
+ $capability = apply_filters( 'w3tc_ajax_capability_' . Util_Request::get_string( 'w3tc_action' ), $base_capability );
161
+ if ( ! empty( $capability ) && ! current_user_can( $capability ) ) {
 
162
  throw new \Exception( 'no permissions' );
163
+ }
164
 
165
  do_action( 'w3tc_ajax' );
166
  do_action( 'w3tc_ajax_' . Util_Request::get_string( 'w3tc_action' ) );
171
  exit();
172
  }
173
 
174
+ /**
175
+ * Load action
176
+ *
177
+ * @throws Exception Exception.
178
+ *
179
+ * @return void
180
+ */
181
  public function wp_ajax_w3tc_monitoring_score() {
182
+ if ( ! $this->_config->get_boolean( 'widget.pagespeed.show_in_admin_bar' ) ) {
183
  exit();
184
+ }
185
 
186
  $score = '';
187
 
188
  $modules = Dispatcher::component( 'ModuleStatus' );
189
+ $score = apply_filters( 'w3tc_monitoring_score', $score );
190
 
191
+ header( 'Content-Type: application/x-javascript; charset=UTF-8' );
192
  echo 'document.getElementById("w3tc_monitoring_score") && ( document.getElementById("w3tc_monitoring_score").innerHTML = "' .
193
  esc_html( strtr( $score, '"', '.' ) ) . '" );';
194
 
200
  *
201
  * @return void
202
  */
203
+ public function admin_init() {
204
+ // Special handling for deactivation link, it's plugins.php file.
205
+ if ( 'w3tc_deactivate_plugin' === Util_Request::get_string( 'action' ) ) {
206
  Util_Activation::deactivate_plugin();
207
  }
208
 
220
  wp_register_style( 'w3tc-lightbox', plugins_url( 'pub/css/lightbox.css', W3TC_FILE ), array(), W3TC_VERSION );
221
  wp_register_style( 'w3tc-widget', plugins_url( 'pub/css/widget.css', W3TC_FILE ), array(), W3TC_VERSION );
222
 
223
+ wp_register_script( 'w3tc-metadata', plugins_url( 'pub/js/metadata.js', W3TC_FILE ), array(), W3TC_VERSION, false );
224
+ wp_register_script( 'w3tc-options', plugins_url( 'pub/js/options.js', W3TC_FILE ), array(), W3TC_VERSION, false );
225
+ wp_register_script( 'w3tc-lightbox', plugins_url( 'pub/js/lightbox.js', W3TC_FILE ), array(), W3TC_VERSION, false );
226
+ wp_register_script( 'w3tc-widget', plugins_url( 'pub/js/widget.js', W3TC_FILE ), array(), W3TC_VERSION, false );
227
+ wp_register_script( 'w3tc-jquery-masonry', plugins_url( 'pub/js/jquery.masonry.min.js', W3TC_FILE ), array( 'jquery' ), W3TC_VERSION, false );
228
 
229
  // New feature count for the Feature Showcase.
230
  wp_register_script( 'w3tc-feature-counter', plugins_url( 'pub/js/feature-counter.js', W3TC_FILE ), array(), W3TC_VERSION, true );
240
  wp_enqueue_script( 'w3tc-feature-counter' );
241
 
242
  // Messages.
243
+ if ( ! is_null( $this->w3tc_message ) && isset( $this->w3tc_message['actions'] ) && is_array( $this->w3tc_message['actions'] ) ) {
244
+ foreach ( $this->w3tc_message['actions'] as $action ) {
 
 
245
  do_action( 'w3tc_message_action_' . $action );
246
+ }
247
  }
248
+
249
+ // For testing.
250
  $w3tc_message_action_val = Util_Request::get_string( 'w3tc_message_action' );
251
  if ( ! empty( $w3tc_message_action_val ) ) {
252
  do_action( 'w3tc_message_action_' . $w3tc_message_action_val );
253
  }
254
  }
255
 
256
+ /**
257
+ * Define icon styles for the custom post type.
258
+ */
259
+ public function admin_head() {
260
+ global $wp_version;
261
+ global $wpdb;
262
+
263
  $page = Util_Request::get_string( 'page', null );
264
 
265
  if ( ( ! is_multisite() || is_super_admin() ) && false !== strpos( $page, 'w3tc' ) && 'w3tc_setup_guide' !== $page && ! get_site_option( 'w3tc_setupguide_completed' ) ) {
267
  $state_master = Dispatcher::config_state_master();
268
 
269
  if ( ! $config->get_boolean( 'pgcache.enabled' ) && $state_master->get_integer( 'common.install' ) > strtotime( 'NOW - 1 WEEK' ) ) {
270
+ wp_safe_redirect( esc_url( network_admin_url( 'admin.php?page=w3tc_setup_guide' ) ) );
271
  }
272
  }
273
 
274
  if ( 'w3tc_dashboard' === $page ) {
275
+ ?>
276
  <script type="text/javascript">
277
+ jQuery( function() {
278
+ jQuery('#normal-sortables').masonry( {
279
+ itemSelector: '.postbox'
280
+ } );
281
+ } );
282
  </script>
283
  <?php
284
  }
285
 
286
  if ( $this->_config->get_boolean( 'common.track_usage' ) && $this->is_w3tc_page ) {
287
+
288
  $current_user = wp_get_current_user();
289
+ $page = Util_Request::get_string( 'page' );
290
+ if ( 'w3tc_extensions' === $page ) {
291
  $page = 'extensions/' . Util_Request::get_string( 'extension' );
292
+ }
293
 
294
+ if ( defined( 'W3TC_DEBUG' ) && W3TC_DEBUG ) {
295
  $profile = 'UA-2264433-7';
296
+ } else {
297
  $profile = 'UA-2264433-8';
298
+ }
299
 
300
  $state = Dispatcher::config_state();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
+ ?>
303
+ <script type="text/javascript">
304
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
305
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
306
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
307
+ })(window,document,'script','https://api.w3-edge.com/v1/analytics','w3tc_ga');
308
+
309
+ if (window.w3tc_ga) {
310
+ w3tc_ga('create', '<?php echo esc_html( $profile ); ?>', 'auto');
311
+ w3tc_ga('set', {
312
+ 'dimension1': 'w3-total-cache',
313
+ 'dimension2': '<?php echo esc_html( W3TC_VERSION ); ?>',
314
+ 'dimension3': '<?php echo esc_html( $wp_version ); ?>',
315
+ 'dimension4': 'php<?php echo esc_html( phpversion() ); ?>',
316
+ 'dimension5': '<?php echo esc_attr( isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '' ); ?>',
317
+ 'dimension6': 'mysql<?php echo esc_attr( $wpdb->db_version() ); ?>',
318
+ 'dimension7': '<?php echo esc_url( Util_Environment::home_url_host() ); ?>',
319
+ 'dimension9': '<?php echo esc_attr( $state->get_string( 'common.install_version' ) ); ?>',
320
+ 'dimension10': '<?php echo esc_attr( Util_Environment::w3tc_edition( $this->_config ) ); ?>',
321
+ 'dimension11': '<?php echo esc_attr( Util_Widget::list_widgets() ); ?>',
322
+
323
+ 'page': '<?php echo esc_attr( $page ); ?>'
324
+ });
325
+
326
+ w3tc_ga('send', 'pageview');
327
+ }
328
  </script>
329
  <?php
330
  }
331
 
332
+ ?>
333
  <style type="text/css" media="screen">
334
+ li.toplevel_page_w3tc_dashboard .wp-menu-image:before{
335
+ content:'\0041';
336
+ top: 2px;
337
+ font-family: 'w3tc';
338
+ }
339
  </style>
340
  <script>
341
+ jQuery(document).ready( function($) {
342
+ $('#toplevel_page_w3tc_dashboard ul li').find('a[href*="w3tc_faq"]')
343
+ .prop('target','_blank')
344
+ .prop('href', <?php echo wp_json_encode( W3TC_FAQ_URL ); ?>);
345
+ } );
346
  </script>
347
  <?php
348
  }
349
 
350
+ /**
351
+ * Render network admin menu.
352
+ */
353
+ public function network_admin_menu() {
354
  $this->_admin_menu( 'manage_network_options' );
355
  }
356
 
357
+ /**
358
+ * Render admin menu.
359
+ */
360
+ public function admin_menu() {
361
  $this->_admin_menu( 'manage_options' );
362
  }
363
 
364
  /**
365
  * Admin menu
366
  *
367
+ * @param string $base_capability Base compatibility.
368
+ *
369
  * @return void
370
  */
371
  private function _admin_menu( $base_capability ) {
372
+ $base_capability = apply_filters( 'w3tc_capability_menu', $base_capability );
 
373
 
374
  if ( current_user_can( $base_capability ) ) {
375
+ $menus = Dispatcher::component( 'Root_AdminMenu' );
376
  $submenu_pages = $menus->generate( $base_capability );
377
 
378
  /**
379
  * Only admin can modify W3TC settings
380
  */
381
  foreach ( $submenu_pages as $submenu_page ) {
382
+ add_action( 'load-' . $submenu_page, array( $this, 'load' ) );
383
+ add_action( 'admin_print_styles-' . $submenu_page, array( $this, 'admin_print_styles' ) );
384
+ add_action( 'admin_print_scripts-' . $submenu_page, array( $this, 'admin_print_scripts' ) );
 
 
 
 
 
385
  }
386
 
387
  global $pagenow;
388
+ if ( 'plugins.php' === $pagenow ) {
389
  add_action( 'admin_print_scripts', array( $this, 'load_plugins_page_js' ) );
390
  add_action( 'admin_print_styles', array( $this, 'print_plugins_page_css' ) );
391
  }
395
  ! empty( Util_Request::get_string( 'w3tc_note' ) ) ||
396
  ! empty( Util_Request::get_string( 'w3tc_error' ) ) ||
397
  ! empty( Util_Request::get_string( 'w3tc_message' ) ) ) {
398
+
399
+ // Only admin can see W3TC notices and errors.
400
+ add_action( 'admin_notices', array( $this, 'admin_notices' ) );
401
+ add_action( 'network_admin_notices', array( $this, 'admin_notices' ) );
 
 
 
 
 
 
 
402
  }
403
  }
404
  }
408
  *
409
  * @return void
410
  */
411
+ public function admin_print_styles() {
412
  wp_enqueue_style( 'w3tc-options' );
413
  wp_enqueue_style( 'w3tc-lightbox' );
414
  }
416
  /**
417
  * Print scripts.
418
  */
419
+ public function admin_print_scripts() {
420
  wp_enqueue_script( 'w3tc-metadata' );
421
  wp_enqueue_script( 'w3tc-options' );
422
  wp_enqueue_script( 'w3tc-lightbox' );
434
  array(
435
  'cdnEnabled' => $this->_config->get_boolean( 'cdn.enabled' ),
436
  'cdnEngine' => $this->_config->get_string( 'cdn.engine' ),
437
+ 'cdnFlushManually' => $this->_config->get_boolean(
438
+ 'cdn.flush_manually',
439
+ Cdn_Util::get_flush_manually_default_override( $this->_config->get_string( 'cdn.engine' ) )
440
+ ),
441
  'cfWarning' => wp_kses(
442
  sprintf(
443
+ // translators: 1: HTML opening a tag to docs.aws.amazon.com for invalidation payments, 2: HTML closing a tag followed by HTML line break tag,
444
+ // translators: 4: HTML line break tag, 5: HTML opening a tag to purge CDN manually, 6: HTML closing a tag.
445
  __(
446
+ 'Please see %1$sAmazon\'s CloudFront documentation -- Paying for file invalidation%2$sThe first 1,000 invalidation paths that you submit per month are free; you pay for each invalidation path over 1,000 in a month.%3$sYou can disable automatic purging by enabling %4$sOnly purge CDN manually%5$s.',
447
  'w3-total-cache'
448
  ),
 
449
  '<a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#PayingForInvalidation">',
450
+ '</a>.<br/>',
451
+ '<br/>',
452
+ '<a href="' . esc_url( admin_url( 'admin.php?page=w3tc_cdn#advanced' ) ) . '">',
453
+ '</a>'
454
  ),
455
  array(
456
  'a' => array(
465
  }
466
 
467
  switch ( $this->_page ) {
468
+ case 'w3tc_minify':
469
+ case 'w3tc_cachegroups':
470
+ wp_enqueue_script(
471
+ 'w3tc_cachegroups',
472
+ plugins_url( 'CacheGroups_Plugin_Admin_View.js', W3TC_FILE ),
473
+ array(
474
+ 'jquery',
475
+ 'jquery-ui-sortable',
476
+ ),
477
+ W3TC_VERSION,
478
+ true
479
+ );
480
+ // No break.
481
+ case 'w3tc_cdn':
482
+ wp_enqueue_script( 'jquery-ui-sortable' );
483
+ break;
484
  }
485
+
486
+ if ( 'w3tc_cdn' === $this->_page ) {
487
  wp_enqueue_script( 'jquery-ui-dialog' );
488
+ }
489
+
490
+ if ( 'w3tc_dashboard' === $this->_page ) {
491
  wp_enqueue_script( 'w3tc-jquery-masonry' );
492
+ }
493
  }
494
 
495
+ /**
496
+ * Load plugins page JS.
497
+ */
498
+ public function load_plugins_page_js() {
499
  wp_enqueue_script( 'w3tc-options' );
500
  }
501
 
502
+ /**
503
+ * Load plugins page CSS.
504
+ */
505
+ public function print_plugins_page_css() {
506
+ ?>
507
+ <style type=\"text/css\">
508
  .w3tc-missing-files ul {
509
  margin-left: 20px;
510
  list-style-type: disc;
527
  width: 170px;
528
  display: inline-block;
529
  }
530
+ </style>
531
+ <?php
532
  }
533
 
534
  /**
535
+ * Contextual help list filter.
 
 
 
536
  */
537
+ public function add_help_tabs() {
538
+ $screen = get_current_screen();
539
  $sections = Generic_Faq::sections();
540
+ $n = 0;
541
 
542
  foreach ( $sections as $section => $data ) {
543
+ $content = '<div class="w3tchelp_content" data-section="' . $section . '"></div>';
544
+
545
+ $screen->add_help_tab(
546
+ array(
547
+ 'id' => 'w3tc_faq_' . $n,
548
+ 'title' => $section,
549
+ 'content' => $content,
550
+ )
551
+ );
552
  $n++;
553
  }
554
  }
555
 
556
+ /**
557
+ * FAQ ajax handler.
558
+ */
559
  public function w3tc_ajax_faq() {
560
  $section = Util_Request::get_string( 'section' );
561
 
562
+ $entries = Generic_Faq::parse( $section );
563
  $response = array();
564
 
565
  ob_start();
567
  $content = ob_get_contents();
568
  ob_end_clean();
569
 
570
+ echo wp_json_encode( array( 'content' => $content ) );
571
  }
572
 
 
 
573
  /**
574
  * Plugin action links filter
575
  *
576
+ * @param array $links Links array.
577
+ *
578
  * @return array
579
  */
580
+ public function plugin_action_links( $links ) {
581
+ array_unshift( $links, '<a class="edit" href="admin.php?page=w3tc_general">' . esc_html__( 'Settings', 'w3-total-cache' ) . '</a>' );
582
+ array_unshift( $links, '<a class="edit" style="color: red" href="admin.php?page=w3tc_support">' . esc_html__( 'Premium Support', 'w3-total-cache' ) . '</a>' );
 
 
 
583
 
584
+ if ( ! is_writable( WP_CONTENT_DIR ) || ! is_writable( Util_Rule::get_browsercache_rules_cache_path() ) ) {
 
585
  $delete_link = '<a href="' .
586
  wp_nonce_url( admin_url( 'plugins.php?action=w3tc_deactivate_plugin' ), 'w3tc' ) .
587
  '">Uninstall</a>';
592
  }
593
 
594
  /**
595
+ * Favorite actions filter.
596
+ *
597
+ * @param array $actions Actions.
598
  *
 
599
  * @return array
600
  */
601
+ public function favorite_actions( $actions ) {
602
+ $actions[ wp_nonce_url( admin_url( 'admin.php?page=w3tc_dashboard&amp;w3tc_flush_all' ), 'w3tc' ) ] = array(
603
  __( 'Empty Caches', 'w3-total-cache' ),
604
+ apply_filters( 'w3tc_capability_favorite_action_flush_all', 'manage_options' ),
605
  );
606
 
607
  return $actions;
610
  /**
611
  * Active plugins pre update option filter
612
  *
613
+ * @param string $new_value New value.
614
+ *
615
  * @return string
616
  */
617
+ public function pre_update_option_active_plugins( $new_value ) {
618
  $old_value = (array) get_option( 'active_plugins' );
619
 
620
+ if ( $new_value !== $old_value && in_array( W3TC_FILE, (array) $new_value, true ) && in_array( W3TC_FILE, (array) $old_value, true ) ) {
621
  $state_note = Dispatcher::config_state_note();
622
  $state_note->set( 'common.show_note.plugins_updated', true );
623
  }
630
  *
631
  * @return void
632
  */
633
+ public function in_plugin_update_message() {
634
  $response = Util_Http::get( W3TC_README_URL );
635
 
636
+ if ( is_wp_error( $response ) || 200 !== $response['response']['code'] ) {
637
  return;
638
+ }
639
 
640
  $matches = null;
641
+ $regexp = '~==\s*Changelog\s*==\s*=\s*[0-9.]+\s*=(.*)(=\s*' . preg_quote( W3TC_VERSION, '~' ) . '\s*=|$)~Uis';
642
 
643
  $body = $response['body'];
644
+ if ( ! preg_match( $regexp, $body, $matches ) ) {
645
  return;
646
+ }
647
 
648
  $changelog = (array) preg_split( '~[\r\n]+~', trim( $matches[1] ) );
649
 
652
 
653
  foreach ( $changelog as $index => $line ) {
654
  if ( preg_match( '~^\s*\*\s*~', $line ) ) {
655
+ if ( ! $ul ) {
656
  echo '<ul style="list-style: disc; margin-left: 20px;margin-top:0;">';
657
  $ul = true;
658
  }
659
  $line = preg_replace( '~^\s*\*\s*~', '', htmlspecialchars( $line ) );
660
+ echo '<li style="width: 50%; margin: 0; float: left; ' . ( 0 === $index % 2 ? 'clear: left;' : '' ) . '">' . esc_html( $line ) . '</li>';
661
  } else {
662
  if ( $ul ) {
663
  echo '</ul><div style="clear: left;"></div>';
666
  }
667
  }
668
 
669
+ if ( $ul ) {
670
  echo '</ul><div style="clear: left;"></div>';
671
+ }
672
 
673
  echo '</div>';
674
  }
678
  *
679
  * @return void
680
  */
681
+ public function admin_notices() {
682
  $cookie_domain = Util_Admin::get_cookie_domain();
683
 
684
  $error_messages = array(
685
+ 'fancy_permalinks_disabled_pgcache' => sprintf(
686
+ // translators: 1 enable button link.
687
+ __(
688
+ 'Fancy permalinks are disabled. Please %1$s it first, then re-attempt to enabling enhanced disk mode.',
689
+ 'w3-total-cache'
690
+ ),
691
+ Util_Ui::button_link( 'enable', 'options-permalink.php' )
692
+ ),
693
+ 'fancy_permalinks_disabled_browsercache' => sprintf(
694
+ // translators: 1 enable button link.
695
+ __(
696
+ 'Fancy permalinks are disabled. Please %1$s it first, then re-attempt to enabling the \'Do not process 404 errors for static objects with WordPress\'.',
697
+ 'w3-total-cache'
698
+ ),
699
+ Util_Ui::button_link( 'enable', 'options-permalink.php' )
700
+ ),
701
+ 'support_request_type' => __( 'Please select request type.', 'w3-total-cache' ),
702
+ 'support_request_url' => sprintf(
703
+ // translators: 1 HTML acronym URL (uniform resource locator).
704
+ __(
705
+ 'Please enter the address of the site in the site %1$s field.',
706
+ 'w3-total-cache'
707
+ ),
708
+ '<acronym title="' . esc_attr__( 'Uniform Resource Locator', 'w3-total-cache' ) . '">' . esc_html__( 'URL', 'w3-total-cache' ) . '</acronym>'
709
+ ),
710
+ 'support_request_name' => __( 'Please enter your name in the Name field', 'w3-total-cache' ),
711
+ 'support_request_email' => __( 'Please enter valid email address in the E-Mail field.', 'w3-total-cache' ),
712
+ 'support_request_phone' => __( 'Please enter your phone in the phone field.', 'w3-total-cache' ),
713
+ 'support_request_subject' => __( 'Please enter subject in the subject field.', 'w3-total-cache' ),
714
+ 'support_request_description' => __( 'Please describe the issue in the issue description field.', 'w3-total-cache' ),
715
+ 'support_request_wp_login' => __( 'Please enter an administrator login. Create a temporary one just for this support case if needed.', 'w3-total-cache' ),
716
+ 'support_request_wp_password' => __( 'Please enter WP Admin password, be sure it\'s spelled correctly.', 'w3-total-cache' ),
717
+ 'support_request_ftp_host' => sprintf(
718
+ // translators: 1 HTML acronym SSH (secure shell), 2 HTML acronym FTP (file transfer protocol).
719
+ __(
720
+ 'Please enter %1$s or %2$s host for the site.',
721
+ 'w3-total-cache'
722
+ ),
723
+ '<acronym title="' . esc_attr__( 'Secure Shell', 'w3-total-cache' ) . '">' . esc_html__( 'SSH', 'w3-total-cache' ) . '</acronym>',
724
+ '<acronym title="' . esc_attr__( 'File Transfer Protocol', 'w3-total-cache' ) . '">' . esc_html__( 'FTP', 'w3-total-cache' ) . '</acronym>'
725
+ ),
726
+ 'support_request_ftp_login' => sprintf(
727
+ // translators: 1 HTML acronym SSH (secure shell), 2 HTML acronym FTP (file transfer protocol).
728
+ __(
729
+ 'Please enter %1$s or %2$s login for the server. Create a temporary one just for this support case if needed.',
730
+ 'w3-total-cache'
731
+ ),
732
+ '<acronym title="' . esc_attr__( 'Secure Shell', 'w3-total-cache' ) . '">' . esc_html__( 'SSH', 'w3-total-cache' ) . '</acronym>',
733
+ '<acronym title="' . esc_attr__( 'File Transfer Protocol', 'w3-total-cache' ) . '">' . esc_html__( 'FTP', 'w3-total-cache' ) . '</acronym>'
734
+ ),
735
+ 'support_request_ftp_password' => sprintf(
736
+ // translators: 1 HTML acronym SSH (secure shell), 2 HTML acronym FTP (file transfer protocol).
737
+ __(
738
+ 'Please enter %1$s or %2$s password for the %2$s account.',
739
+ 'w3-total-cache'
740
+ ),
741
+ '<acronym title="' . esc_attr__( 'Secure Shell', 'w3-total-cache' ) . '">' . esc_html__( 'SSH', 'w3-total-cache' ) . '</acronym>',
742
+ '<acronym title="' . esc_attr__( 'File Transfer Protocol', 'w3-total-cache' ) . '">' . esc_html__( 'FTP', 'w3-total-cache' ) . '</acronym>'
743
+ ),
744
+ 'support_request' => __( 'Unable to send the support request.', 'w3-total-cache' ),
745
+ 'config_import_no_file' => __( 'Please select config file.', 'w3-total-cache' ),
746
+ 'config_import_upload' => __( 'Unable to upload config file.', 'w3-total-cache' ),
747
+ 'config_import_import' => __( 'Configuration file could not be imported.', 'w3-total-cache' ),
748
+ 'config_reset' => sprintf(
749
+ // translators: 1 W3TC config director path.
750
+ __(
751
+ 'Default settings could not be restored. Please run %1$s to make the configuration file write-able, then try again.',
752
+ 'w3-total-cache'
753
+ ),
754
+ '<strong>chmod 777 ' . W3TC_CONFIG_DIR . '</strong>'
755
+ ),
756
+ 'cdn_purge_attachment' => __( 'Unable to purge attachment.', 'w3-total-cache' ),
757
+ 'pgcache_purge_post' => __( 'Unable to purge post.', 'w3-total-cache' ),
758
+ 'enable_cookie_domain' => sprintf(
759
+ // translators: 1 absolute path to wp-config.php, 2 cookie domain definition, 3 require once wp-setting.php definition.
760
+ __(
761
+ '%1$s could not be written, please edit config and add: %2$s before %3$s.',
762
+ 'w3-total-cache'
763
+ ),
764
+ '<strong>' . ABSPATH . 'wp-config.php</strong>',
765
+ '<br /><strong style="color:#f00;">define(\'COOKIE_DOMAIN\', \'' . addslashes( $cookie_domain ) . '\');</strong>',
766
+ '<strong style="color:#f00;">require_once(ABSPATH . \'wp-settings.php\');</strong>'
767
+ ),
768
+ 'disable_cookie_domain' => sprintf(
769
+ // translators: 1 absolute path to wp-config.php, 2 cooke domain definition, 3 require once wp-setting.php definition.
770
+ __(
771
+ '%1$s could not be written, please edit config and add:%2$s before %3$s.',
772
+ 'w3-total-cache'
773
+ ),
774
+ '<strong>' . ABSPATH . 'wp-config.php</strong>',
775
+ '<br /><strong style="color:#f00;">define(\'COOKIE_DOMAIN\', false);</strong>',
776
+ '<strong style="color:#f00;">require_once(ABSPATH . \'wp-settings.php\');</strong>'
777
+ ),
778
+ 'pull_zone' => __( 'Pull Zone could not be automatically created.', 'w3-total-cache' ),
779
  );
780
 
781
  $note_messages = array(
782
+ 'config_save' => __( 'Plugin configuration successfully updated.', 'w3-total-cache' ),
783
+ 'flush_all' => __( 'All caches successfully emptied.', 'w3-total-cache' ),
784
+ 'flush_memcached' => __( 'Memcached cache(s) successfully emptied.', 'w3-total-cache' ),
785
+ 'flush_opcode' => __( 'Opcode cache(s) successfully emptied.', 'w3-total-cache' ),
786
+ 'flush_file' => __( 'Disk cache(s) successfully emptied.', 'w3-total-cache' ),
787
+ 'flush_pgcache' => __( 'Page cache successfully emptied.', 'w3-total-cache' ),
788
+ 'flush_dbcache' => __( 'Database cache successfully emptied.', 'w3-total-cache' ),
789
+ 'flush_objectcache' => __( 'Object cache successfully emptied.', 'w3-total-cache' ),
790
+ 'flush_fragmentcache' => __( 'Fragment cache successfully emptied.', 'w3-total-cache' ),
791
+ 'flush_minify' => __( 'Minify cache successfully emptied.', 'w3-total-cache' ),
792
+ 'flush_browser_cache' => __( 'Media Query string has been successfully updated.', 'w3-total-cache' ),
793
+ 'flush_varnish' => __( 'Varnish servers successfully purged.', 'w3-total-cache' ),
794
+ 'flush_cdn' => sprintf(
795
+ // translators: 1 HTML acronym for CDN (content delivery network).
796
+ __(
797
+ '%1$s was successfully purged.',
798
+ 'w3-total-cache'
799
+ ),
800
+ '<acronym title="' . esc_attr__( 'Content Delivery Network', 'w3-total-cache' ) . '">' . esc_html__( 'CDN', 'w3-total-cache' ) . '</acronym>'
801
+ ),
802
+ 'support_request' => __( 'The support request has been successfully sent.', 'w3-total-cache' ),
803
+ 'config_import' => __( 'Settings successfully imported.', 'w3-total-cache' ),
804
+ 'config_reset' => __( 'Settings successfully restored.', 'w3-total-cache' ),
805
+ 'preview_enable' => __( 'Preview mode was successfully enabled', 'w3-total-cache' ),
806
+ 'preview_disable' => __( 'Preview mode was successfully disabled', 'w3-total-cache' ),
807
+ 'preview_deploy' => __( 'Preview settings successfully deployed. Preview mode remains enabled until it\'s disabled. Continue testing new settings or disable preview mode if done.', 'w3-total-cache' ),
808
  'cdn_purge_attachment' => __( 'Attachment successfully purged.', 'w3-total-cache' ),
809
+ 'pgcache_purge_post' => __( 'Post successfully purged.', 'w3-total-cache' ),
810
+ 'new_relic_save' => __( 'New relic settings have been updated.', 'w3-total-cache' ),
811
+ 'add_in_removed' => __( 'The add-in has been removed.', 'w3-total-cache' ),
812
+ 'enabled_edge' => __( 'Edge mode has been enabled.', 'w3-total-cache' ),
813
+ 'disabled_edge' => __( 'Edge mode has been disabled.', 'w3-total-cache' ),
814
+ 'pull_zone' => __( 'Pull Zone was automatically created.', 'w3-total-cache' ),
815
  );
816
 
817
+ $errors = array();
818
+ $notes = array();
819
  $environment_error_present = false;
820
 
821
  $error = Util_Request::get_string( 'w3tc_error' );
822
+ if ( isset( $error_messages[ $error ] ) ) {
823
+ $errors[ $error ] = $error_messages[ $error ];
824
+ }
825
 
826
  $note = Util_Request::get_string( 'w3tc_note' );
827
+ if ( isset( $note_messages[ $note ] ) ) {
828
+ $notes[ $note ] = $note_messages[ $note ];
829
+ }
830
 
831
  // print errors happened during last request execution,
832
  // when we decided to redirect with error message instead of
833
+ // printing it directly (to avoid reexecution on refresh).
834
+ if ( ! is_null( $this->w3tc_message ) ) {
835
  $v = $this->w3tc_message;
836
  if ( isset( $v['errors'] ) && is_array( $v['errors'] ) ) {
837
  foreach ( $v['errors'] as $error ) {
838
+ if ( isset( $error_messages[ $error ] ) ) {
839
+ $errors[] = $error_messages[ $error ];
840
+ } else {
841
  $errors[] = $error;
842
+ }
843
  }
844
  }
845
  if ( isset( $v['notes'] ) && is_array( $v['notes'] ) ) {
846
  foreach ( $v['notes'] as $note ) {
847
+ if ( isset( $note_messages[ $note ] ) ) {
848
+ $notes[] = $note_messages[ $note ];
849
+ } else {
850
  $notes[] = $note;
851
+ }
852
  }
853
  }
854
  }
855
 
856
  /*
857
+ * Filesystem environment fix, if needed.
858
  */
859
  try {
860
  $environment = Dispatcher::component( 'Root_Environment' );
868
  $n = 1;
869
 
870
  foreach ( $r['before_errors'] as $e ) {
871
+ $errors[ 'generic_env_' . $n ] = $e;
872
  $n++;
873
  }
874
 
877
  'background: white; ' .
878
  'margin: 10px 30px 10px 30px; ' .
879
  'padding: 10px; display: none';
880
+
881
  $ftp_style = 'border: 1px solid black; background: white; ' .
882
  'margin: 10px 30px 10px 30px; ' .
883
  'padding: 10px; display: none';
884
+
885
+ $ftp_form = str_replace( 'class="wrap"', '', $exs->credentials_form() );
886
+ $ftp_form = str_replace( '<form ', '<form name="w3tc_ftp_form" ', $ftp_form );
 
887
  $ftp_form = str_replace( '<fieldset>', '', $ftp_form );
888
  $ftp_form = str_replace( '</fieldset>', '', $ftp_form );
889
+ $ftp_form = str_replace( 'id="upgrade" class="button"', 'id="upgrade" class="button w3tc-button-save"', $ftp_form );
 
890
 
891
+ $error = '<strong>' . esc_html__( 'W3 Total Cache Error:', 'w3-total-cache' ) . '</strong> ' .
892
+ esc_html__( 'Files and directories could not be automatically created to complete the installation.', 'w3-total-cache' ) .
 
893
  '<table>' .
894
  '<tr>' .
895
+ '<td>' . esc_html__( 'Please execute commands manually', 'w3-total-cache' ) . '</td>' .
896
+ '<td>' . Util_Ui::button( __( 'View required changes', 'w3-total-cache' ), '', 'w3tc-show-required-changes' ) . '</td>' .
 
 
 
897
  '</tr>' .
898
  '<tr>' .
899
+ '<td>' . esc_html__( 'or use FTP form to allow ', 'w3-total-cache' ) .
900
+ '<strong>' . esc_html__( 'W3 Total Cache', 'w3-total-cache' ) . '</strong>' .
901
+ esc_html__( 'make it automatically.', 'w3-total-cache' ) .
902
+ '</td>' .
903
+ '<td>' . Util_Ui::button( 'Update via FTP', '', 'w3tc-show-ftp-form' ) . '</td>' .
904
+ '</tr>' .
905
+ '</table>' .
906
+ '<div class="w3tc-required-changes" style="' . $changes_style . '">' . $r['required_changes'] . '</div>' .
907
+ '<div class="w3tc-ftp-form" style="' . $ftp_style . '">' . $ftp_form . '</div>';
 
 
 
908
 
909
  $environment_error_present = true;
910
+ $errors['generic_ftp'] = $error;
911
  }
912
 
913
  foreach ( $r['later_errors'] as $e ) {
950
  printf(
951
  '<div class="error w3tc_error" id="%1$s"><p>%2$s</p></div>',
952
  esc_attr( $key ),
953
+ $error // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
954
  );
955
  }
956
  }
Root_Loader.php CHANGED
@@ -1,6 +1,17 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
 
 
 
 
 
4
  class Root_Loader {
5
  /**
6
  * Enabled Plugins that has been run
@@ -8,6 +19,7 @@ class Root_Loader {
8
  * @var W3_Plugin[]
9
  */
10
  private $_loaded_plugins = array();
 
11
  /**
12
  * Enabled extensions that has been run
13
  *
@@ -15,32 +27,54 @@ class Root_Loader {
15
  */
16
  private $_loaded_extensions = array();
17
 
18
- function __construct() {
 
 
 
19
  $c = Dispatcher::config();
20
 
21
- $plugins = array();
22
  $plugins[] = new Generic_Plugin();
23
 
24
- if ( $c->get_boolean( 'dbcache.enabled' ) )
25
  $plugins[] = new DbCache_Plugin();
26
- if ( $c->get_boolean( 'objectcache.enabled' ) )
 
 
27
  $plugins[] = new ObjectCache_Plugin();
28
- if ( $c->get_boolean( 'pgcache.enabled' ) )
 
 
29
  $plugins[] = new PgCache_Plugin();
30
- if ( $c->get_boolean( 'cdn.enabled' ) )
 
 
31
  $plugins[] = new Cdn_Plugin();
32
- if ( $c->get_boolean( 'cdnfsd.enabled' ) )
 
 
33
  $plugins[] = new Cdnfsd_Plugin();
34
- if ( $c->get_boolean( 'lazyload.enabled' ) )
 
 
35
  $plugins[] = new UserExperience_LazyLoad_Plugin();
36
- if ( $c->get_boolean( 'browsercache.enabled' ) )
 
 
37
  $plugins[] = new BrowserCache_Plugin();
38
- if ( $c->get_boolean( 'minify.enabled' ) )
 
 
39
  $plugins[] = new Minify_Plugin();
40
- if ( $c->get_boolean( 'varnish.enabled' ) )
 
 
41
  $plugins[] = new Varnish_Plugin();
42
- if ( $c->get_boolean( 'stats.enabled' ) )
 
 
43
  $plugins[] = new UsageStatistics_Plugin();
 
44
 
45
  if ( is_admin() ) {
46
  $plugins[] = new Generic_Plugin_Admin();
@@ -57,20 +91,22 @@ class Root_Loader {
57
 
58
  $plugins[] = new Cdn_Plugin_Admin();
59
  $plugins[] = new Cdnfsd_Plugin_Admin();
 
60
  $cdn_engine = $c->get_string( 'cdn.engine' );
61
- if ( $cdn_engine == 'maxcdn' ) {
62
  $plugins[] = new Cdn_Plugin_WidgetMaxCdn();
63
  }
64
 
65
- if ( $c->get_boolean( 'widget.pagespeed.enabled' ) )
66
  $plugins[] = new PageSpeed_Plugin_Widget();
 
67
 
68
  $plugins[] = new Generic_Plugin_AdminCompatibility();
69
  $plugins[] = new Licensing_Plugin_Admin();
70
 
71
- if ( $c->get_boolean( 'pgcache.enabled' ) ||
72
- $c->get_boolean( 'varnish.enabled' ) )
73
  $plugins[] = new Generic_Plugin_AdminRowActions();
 
74
 
75
  $plugins[] = new Extensions_Plugin_Admin();
76
  $plugins[] = new Generic_Plugin_AdminNotifications();
@@ -85,21 +121,21 @@ class Root_Loader {
85
 
86
  $this->_loaded_plugins = $plugins;
87
 
88
- register_activation_hook( W3TC_FILE, array(
89
- $this,
90
- 'activate'
91
- ) );
92
 
93
- register_deactivation_hook( W3TC_FILE, array(
94
- $this,
95
- 'deactivate'
96
- ) );
97
  }
98
 
99
  /**
100
  * Run plugins
101
  */
102
- function run() {
103
  foreach ( $this->_loaded_plugins as $plugin ) {
104
  $plugin->run();
105
  }
@@ -114,6 +150,8 @@ class Root_Loader {
114
 
115
  /**
116
  * Activation action hook
 
 
117
  */
118
  public function activate( $network_wide ) {
119
  Root_AdminActivation::activate( $network_wide );
@@ -129,19 +167,18 @@ class Root_Loader {
129
  /**
130
  * Loads extensions stored in config
131
  */
132
- function run_extensions() {
133
- $c = Dispatcher::config();
134
  $extensions = $c->get_array( 'extensions.active' );
135
 
136
  $frontend = $c->get_array( 'extensions.active_frontend' );
137
  foreach ( $frontend as $extension => $nothing ) {
138
- if ( isset( $extensions[$extension] ) ) {
139
- $path = $extensions[$extension];
140
- $filename = W3TC_EXTENSION_DIR . '/' .
141
- str_replace( '..', '', trim( $path, '/' ) );
142
 
143
  if ( file_exists( $filename ) ) {
144
- include_once( $filename );
145
  }
146
  }
147
  }
@@ -152,7 +189,7 @@ class Root_Loader {
152
  str_replace( '..', '', trim( $path, '/' ) );
153
 
154
  if ( file_exists( $filename ) ) {
155
- include_once( $filename );
156
  }
157
  }
158
  }
@@ -170,51 +207,66 @@ class Root_Loader {
170
  if ( 'never' === $visibility || ( 'extension' === $visibility && ! isset( $extensions['imageservice'] ) ) ) {
171
  add_action(
172
  'pre_get_posts',
173
- function( $query ) {
174
- if ( ! is_admin() || ! $query->is_main_query() ) {
175
- return;
176
- }
177
-
178
- $screen = get_current_screen();
179
-
180
- if ( ! $screen || 'upload' !== $screen->id || 'attachment' !== $screen->post_type ) {
181
- return;
182
- }
183
-
184
- $query->set(
185
- 'meta_query',
186
- array(
187
- array(
188
- 'key' => 'w3tc_imageservice_file',
189
- 'compare' => 'NOT EXISTS',
190
- ),
191
- )
192
- );
193
-
194
- return;
195
- }
196
  );
197
 
198
  add_filter(
199
  'ajax_query_attachments_args',
200
- function( $args ) {
201
- if ( ! is_admin() ) {
202
- return;
203
- }
204
-
205
- // Modify the query.
206
- $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
207
- array(
208
- 'key' => 'w3tc_imageservice_file',
209
- 'compare' => 'NOT EXISTS',
210
- ),
211
- );
212
-
213
- return $args;
214
- }
215
  );
216
  }
217
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  }
219
 
220
  global $w3tc_root;
1
  <?php
2
+ /**
3
+ * File: Root_Loader.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
+ /**
11
+ * Class: Root_Loader
12
+ *
13
+ * phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
14
+ */
15
  class Root_Loader {
16
  /**
17
  * Enabled Plugins that has been run
19
  * @var W3_Plugin[]
20
  */
21
  private $_loaded_plugins = array();
22
+
23
  /**
24
  * Enabled extensions that has been run
25
  *
27
  */
28
  private $_loaded_extensions = array();
29
 
30
+ /**
31
+ * Constructor.
32
+ */
33
+ public function __construct() {
34
  $c = Dispatcher::config();
35
 
36
+ $plugins = array();
37
  $plugins[] = new Generic_Plugin();
38
 
39
+ if ( $c->get_boolean( 'dbcache.enabled' ) ) {
40
  $plugins[] = new DbCache_Plugin();
41
+ }
42
+
43
+ if ( $c->get_boolean( 'objectcache.enabled' ) ) {
44
  $plugins[] = new ObjectCache_Plugin();
45
+ }
46
+
47
+ if ( $c->get_boolean( 'pgcache.enabled' ) ) {
48
  $plugins[] = new PgCache_Plugin();
49
+ }
50
+
51
+ if ( $c->get_boolean( 'cdn.enabled' ) ) {
52
  $plugins[] = new Cdn_Plugin();
53
+ }
54
+
55
+ if ( $c->get_boolean( 'cdnfsd.enabled' ) ) {
56
  $plugins[] = new Cdnfsd_Plugin();
57
+ }
58
+
59
+ if ( $c->get_boolean( 'lazyload.enabled' ) ) {
60
  $plugins[] = new UserExperience_LazyLoad_Plugin();
61
+ }
62
+
63
+ if ( $c->get_boolean( 'browsercache.enabled' ) ) {
64
  $plugins[] = new BrowserCache_Plugin();
65
+ }
66
+
67
+ if ( $c->get_boolean( 'minify.enabled' ) ) {
68
  $plugins[] = new Minify_Plugin();
69
+ }
70
+
71
+ if ( $c->get_boolean( 'varnish.enabled' ) ) {
72
  $plugins[] = new Varnish_Plugin();
73
+ }
74
+
75
+ if ( $c->get_boolean( 'stats.enabled' ) ) {
76
  $plugins[] = new UsageStatistics_Plugin();
77
+ }
78
 
79
  if ( is_admin() ) {
80
  $plugins[] = new Generic_Plugin_Admin();
91
 
92
  $plugins[] = new Cdn_Plugin_Admin();
93
  $plugins[] = new Cdnfsd_Plugin_Admin();
94
+
95
  $cdn_engine = $c->get_string( 'cdn.engine' );
96
+ if ( 'maxcdn' === $cdn_engine ) {
97
  $plugins[] = new Cdn_Plugin_WidgetMaxCdn();
98
  }
99
 
100
+ if ( $c->get_boolean( 'widget.pagespeed.enabled' ) ) {
101
  $plugins[] = new PageSpeed_Plugin_Widget();
102
+ }
103
 
104
  $plugins[] = new Generic_Plugin_AdminCompatibility();
105
  $plugins[] = new Licensing_Plugin_Admin();
106
 
107
+ if ( $c->get_boolean( 'pgcache.enabled' ) || $c->get_boolean( 'varnish.enabled' ) ) {
 
108
  $plugins[] = new Generic_Plugin_AdminRowActions();
109
+ }
110
 
111
  $plugins[] = new Extensions_Plugin_Admin();
112
  $plugins[] = new Generic_Plugin_AdminNotifications();
121
 
122
  $this->_loaded_plugins = $plugins;
123
 
124
+ register_activation_hook(
125
+ W3TC_FILE,
126
+ array( $this, 'activate' )
127
+ );
128
 
129
+ register_deactivation_hook(
130
+ W3TC_FILE,
131
+ array( $this, 'deactivate' )
132
+ );
133
  }
134
 
135
  /**
136
  * Run plugins
137
  */
138
+ public function run() {
139
  foreach ( $this->_loaded_plugins as $plugin ) {
140
  $plugin->run();
141
  }
150
 
151
  /**
152
  * Activation action hook
153
+ *
154
+ * @param bool $network_wide Network wide flag.
155
  */
156
  public function activate( $network_wide ) {
157
  Root_AdminActivation::activate( $network_wide );
167
  /**
168
  * Loads extensions stored in config
169
  */
170
+ public function run_extensions() {
171
+ $c = Dispatcher::config();
172
  $extensions = $c->get_array( 'extensions.active' );
173
 
174
  $frontend = $c->get_array( 'extensions.active_frontend' );
175
  foreach ( $frontend as $extension => $nothing ) {
176
+ if ( isset( $extensions[ $extension ] ) ) {
177
+ $path = $extensions[ $extension ];
178
+ $filename = W3TC_EXTENSION_DIR . '/' . str_replace( '..', '', trim( $path, '/' ) );
 
179
 
180
  if ( file_exists( $filename ) ) {
181
+ include_once $filename;
182
  }
183
  }
184
  }
189
  str_replace( '..', '', trim( $path, '/' ) );
190
 
191
  if ( file_exists( $filename ) ) {
192
+ include_once $filename;
193
  }
194
  }
195
  }
207
  if ( 'never' === $visibility || ( 'extension' === $visibility && ! isset( $extensions['imageservice'] ) ) ) {
208
  add_action(
209
  'pre_get_posts',
210
+ array( $this, 'w3tc_modify_query_obj' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  );
212
 
213
  add_filter(
214
  'ajax_query_attachments_args',
215
+ array( $this, 'w3tc_filter_ajax_args' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  );
217
  }
218
  }
219
+
220
+ /**
221
+ * Modify query object to hide Image Service converted images.
222
+ *
223
+ * @param object $query WP_Query object.
224
+ *
225
+ * @return void
226
+ */
227
+ public function w3tc_modify_query_obj( $query ) {
228
+ if ( ! is_admin() || ! $query->is_main_query() ) {
229
+ return;
230
+ }
231
+
232
+ $screen = get_current_screen();
233
+
234
+ if ( ! $screen || 'upload' !== $screen->id || 'attachment' !== $screen->post_type ) {
235
+ return;
236
+ }
237
+
238
+ $query->set(
239
+ 'meta_query',
240
+ array(
241
+ array(
242
+ 'key' => 'w3tc_imageservice_file',
243
+ 'compare' => 'NOT EXISTS',
244
+ ),
245
+ )
246
+ );
247
+ }
248
+
249
+ /**
250
+ * Filter AJAX query arguments for attachements to hide Image Service converted images.
251
+ *
252
+ * @param string $args arguments.
253
+ *
254
+ * @return void
255
+ */
256
+ public function w3tc_filter_ajax_args( $args ) {
257
+ if ( ! is_admin() ) {
258
+ return;
259
+ }
260
+
261
+ $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
262
+ array(
263
+ 'key' => 'w3tc_imageservice_file',
264
+ 'compare' => 'NOT EXISTS',
265
+ ),
266
+ );
267
+
268
+ return $args;
269
+ }
270
  }
271
 
272
  global $w3tc_root;
inc/options/cdn.php CHANGED
@@ -1,4 +1,10 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
4
  if ( ! defined( 'W3TC' ) ) {
@@ -377,8 +383,8 @@ $upload_blogfiles_enabled = $cdn_mirror || ! is_network_admin() || ! Util_Enviro
377
 
378
  <tr>
379
  <th colspan="2">
380
- <?php $this->checkbox( 'cdn.flush_manually' ); ?>
381
  <?php
 
382
  echo wp_kses(
383
  sprintf(
384
  // translators: 1 opening HTML acronym tag, 2 closing HTML acronym tag.
1
  <?php
2
+ /**
3
+ * File: cdn.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
  if ( ! defined( 'W3TC' ) ) {
383
 
384
  <tr>
385
  <th colspan="2">
 
386
  <?php
387
+ $this->checkbox( 'cdn.flush_manually' );
388
  echo wp_kses(
389
  sprintf(
390
  // translators: 1 opening HTML acronym tag, 2 closing HTML acronym tag.
inc/options/cdn/s3.php CHANGED
@@ -1,4 +1,10 @@
1
  <?php
 
 
 
 
 
 
2
  namespace W3TC;
3
 
4
  if ( ! defined( 'W3TC' ) ) {
@@ -135,9 +141,9 @@ if ( ! defined( 'W3TC' ) ) {
135
  <?php
136
  $cdn_s3_bucket = $this->_config->get_string( 'cdn.s3.bucket' );
137
  if ( '' !== $cdn_s3_bucket ) {
138
- echo esc_html( $cdn_s3_bucket ) . '.s3.amazonaws.com';
139
  } else {
140
- echo '&lt;bucket&gt;.s3.amazonaws.com';
141
  }
142
 
143
  esc_html_e( 'or CNAME:', 'w3-total-cache' );
@@ -175,9 +181,9 @@ if ( ! defined( 'W3TC' ) ) {
175
  </td>
176
  </tr>
177
  <tr>
178
- <th><label for="cdn_s3_public_objects"><?php _e( 'Set objects to publicly accessible on upload:', 'w3-total-cache' ); ?></label></th>
179
  <td>
180
- <select id="cdn_s3_public_objects" name="cdn__s3__public_objects" <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> >
181
  <option value="enabled"<?php selected( $this->_config->get_string( 'cdn.s3.public_objects' ), 'enabled' ); ?>><?php esc_html_e( 'Enabled (apply the \'public-read\' ACL)', 'w3-total-cache' ); ?></option>
182
  <option value="disabled"<?php selected( $this->_config->get_string( 'cdn.s3.public_objects' ), 'disabled' ); ?>><?php esc_html_e( 'Disabled (don\'t apply an ACL)', 'w3-total-cache' ); ?></option>
183
  </select>
1
  <?php
2
+ /**
3
+ * File: s3.php
4
+ *
5
+ * @package W3TC
6
+ */
7
+
8
  namespace W3TC;
9
 
10
  if ( ! defined( 'W3TC' ) ) {
141
  <?php
142
  $cdn_s3_bucket = $this->_config->get_string( 'cdn.s3.bucket' );
143
  if ( '' !== $cdn_s3_bucket ) {
144
+ echo esc_html( $cdn_s3_bucket ) . '.s3.amazonaws.com ';
145
  } else {
146
+ echo '&lt;bucket&gt;.s3.amazonaws.com ';
147
  }
148
 
149
  esc_html_e( 'or CNAME:', 'w3-total-cache' );
181
  </td>
182
  </tr>
183
  <tr>
184
+ <th><label for="cdn_s3_public_objects"><?php esc_html_e( 'Set objects to publicly accessible on upload:', 'w3-total-cache' ); ?></label></th>
185
  <td>
186
+ <select id="cdn_s3_public_objects" name="cdn__s3__public_objects" <?php Util_Ui::sealing_disabled( 'cdn.' ); ?> >
187
  <option value="enabled"<?php selected( $this->_config->get_string( 'cdn.s3.public_objects' ), 'enabled' ); ?>><?php esc_html_e( 'Enabled (apply the \'public-read\' ACL)', 'w3-total-cache' ); ?></option>
188
  <option value="disabled"<?php selected( $this->_config->get_string( 'cdn.s3.public_objects' ), 'disabled' ); ?>><?php esc_html_e( 'Disabled (don\'t apply an ACL)', 'w3-total-cache' ); ?></option>
189
  </select>
pub/js/options.js CHANGED
@@ -321,6 +321,11 @@ function w3tc_csp_reference() {
321
  }
322
 
323
  function cdn_cf_check() {
 
 
 
 
 
324
  var cdnEnabled = jQuery( '#cdn__enabled' ).is( ':checked' ),
325
  cdnEngine = jQuery( '#cdn__engine' ).find( ':selected' ).val(),
326
  cdnFlushManually = jQuery( '[name="cdn__flush_manually"]' ).is( ':checked' );
321
  }
322
 
323
  function cdn_cf_check() {
324
+ // Prevents JS error for non W3TC pages.
325
+ if ( typeof w3tcData === 'undefined' ) {
326
+ return;
327
+ }
328
+
329
  var cdnEnabled = jQuery( '#cdn__enabled' ).is( ':checked' ),
330
  cdnEngine = jQuery( '#cdn__engine' ).find( ':selected' ).val(),
331
  cdnFlushManually = jQuery( '[name="cdn__flush_manually"]' ).is( ':checked' );
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Plugin Name ===
2
  Contributors: boldgrid, fredericktownes, maxicusc, gidomanders, bwmarkle, harryjackson1221, joemoto, vmarko, jacobd91
3
  Tags: seo, cache, CDN, pagespeed, caching, performance, compression, optimize, cloudflare, nginx, apache, varnish, redis, aws, amazon web services, s3, cloudfront, azure
4
- Requires at least: 3.8
5
  Tested up to: 6.0
6
- Stable tag: 2.2.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -285,6 +285,18 @@ Please reach out to all of these people and support their projects if you're so
285
 
286
  == Changelog ==
287
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  = 2.2.4 =
289
  * Fix: Extensions URL in settings
290
  * Fix: Redis undefined array key warnings
1
  === Plugin Name ===
2
  Contributors: boldgrid, fredericktownes, maxicusc, gidomanders, bwmarkle, harryjackson1221, joemoto, vmarko, jacobd91
3
  Tags: seo, cache, CDN, pagespeed, caching, performance, compression, optimize, cloudflare, nginx, apache, varnish, redis, aws, amazon web services, s3, cloudfront, azure
4
+ Requires at least: 5.3
5
  Tested up to: 6.0
6
+ Stable tag: 2.2.6
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
285
 
286
  == Changelog ==
287
 
288
+ = 2.2.6 =
289
+ * Fix: Error clearing all cache when using Cloudfront full CDN in Pro
290
+
291
+ = 2.2.5 =
292
+ * Fix: Revert WooCommerce Variation Image Gallery plugin CDN filter
293
+ * Fix: DB cache syntax error in PHP 5.6
294
+ * Fix: Added missing space to S3 CDN bucket label
295
+ * Fix: JS error for CloudFront CDN related check on non-W3TC pages
296
+ * Fix: Page cache unpack warning for empty/malformed files
297
+ * Enhancement: Image Service pre_get_posts anonymous action now hooked (w3tc_modify_query_obj)
298
+ * Enhancement: Image Service ajax_query_attachments_args anonymous action now hooked (w3tc_filter_ajax_args)
299
+
300
  = 2.2.4 =
301
  * Fix: Extensions URL in settings
302
  * Fix: Redis undefined array key warnings
w3-total-cache-api.php CHANGED
@@ -12,7 +12,7 @@ if ( ! defined( 'ABSPATH' ) ) {
12
  }
13
 
14
  define( 'W3TC', true );
15
- define( 'W3TC_VERSION', '2.2.4' );
16
  define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
17
  define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
18
  define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
12
  }
13
 
14
  define( 'W3TC', true );
15
+ define( 'W3TC_VERSION', '2.2.6' );
16
  define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
17
  define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
18
  define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
w3-total-cache.php CHANGED
@@ -3,8 +3,8 @@
3
  * Plugin Name: W3 Total Cache
4
  * Plugin URI: https://www.boldgrid.com/totalcache/
5
  * Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
6
- * Version: 2.2.4
7
- * Requires at least: 3.8
8
  * Requires PHP: 5.6
9
  * Author: BoldGrid
10
  * Author URI: https://www.boldgrid.com/
3
  * Plugin Name: W3 Total Cache
4
  * Plugin URI: https://www.boldgrid.com/totalcache/
5
  * Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
6
+ * Version: 2.2.6
7
+ * Requires at least: 5.3
8
  * Requires PHP: 5.6
9
  * Author: BoldGrid
10
  * Author URI: https://www.boldgrid.com/