W3 Total Cache - Version 0.9.5

Version Description

  • Fixed XSS vulnerability
  • Fixed issues with dismissing overlays
  • Fixed handling of tilde in URLs
  • Fixed issue with HTTP compression header when using mfunc calls
  • Fixed cache ID issue with minify in network mode
  • Fixed rare issue of caching empty document when some PHP errors occur in themes or plugins
  • Fixed caching of query strings
  • Added support for APCu Opcode Cache
  • Added support for Redis
  • Added support for Google Drive
  • Added support for Amazon S3-compatible stroage services
  • Added support for PECL memcached
  • Added support for srcset elements
  • Added support for Rackspace CDN Origin Pull
  • Added support for minification of external fonts
  • Added support for WOFF2 font format
  • Added support for FTPS (FTP-SSL, S-FTP)
  • Added YUI Compressor's PHP Port of the CSS minifier
  • Added Narcissus' JS minifier
  • Added purge of parent page when attachments are added or updated
  • Added Highwinds CDN provider
  • Added "Validate Timestamps" option for compatible opcode caches functions like apc.stat are enabled
  • Added Full Site Delivery for Pro subscribers
  • Added HTTP Strict Transport Security (HSTS) support
  • Added a sample extension for developers to reference
  • Added Rackspace Cloud Files Multi-Region Support
  • Added more support for exclusions to database cache
  • Added more optionality to minifiers
  • Added WPML Performance Extension
  • Improved PHP 5.6 compatibility
  • Improved PHP 7 compatibility
  • Improved performance menu in admin bar, including purging of specific cache engines and more
  • Improved SSL inter-operability
  • Improved reliablity of test buttons
  • Improved nomenclature of caching files for higher cache hit rates
  • Improved nginx compatibility
  • Improved WP CLI support
  • Improved Cloudflare compatibility (now using latest APIs)
  • Improved AWS API compatibility (now using latest APIs)
  • Improved Rackspace Cloud Files compatibility (now using latest APIs)
  • Improved page cache purge for extensions like cloudflare and other reverse proxy use cases
  • Improved extension framework functionality
  • Improved compatibility of headers like eTag and content encoding
  • Improved template fragment caching
  • Improved notifications, warnings and errors
  • Improved moble user agents detection
  • Improved security with nonces and form elements
  • Improved security throughout the codebase
  • Improved detail of debug messages
  • Improved Amazon SNS security (validation)
  • Improved minify's ability to match script tags without type attribute
Download this release

Release Info

Developer fredericktownes
Plugin Icon 128x128 W3 Total Cache
Version 0.9.5
Comparing to
See all releases

Code changes from version 0.9.4.1 to 0.9.5

Files changed (211) hide show
  1. Base_Page_Settings.php +182 -0
  2. BrowserCache_ConfigLabels.php +41 -0
  3. BrowserCache_Core.php +71 -0
  4. BrowserCache_Environment.php +830 -0
  5. BrowserCache_Page.php +36 -0
  6. BrowserCache_Plugin.php +338 -0
  7. BrowserCache_Plugin_Admin.php +10 -0
  8. Cache.php +201 -0
  9. CacheFlush.php +198 -0
  10. CacheFlush_Locally.php +265 -0
  11. Cache_Apc.php +239 -0
  12. Cache_Apcu.php +238 -0
  13. Cache_Base.php +222 -0
  14. Cache_Eaccelerator.php +229 -0
  15. Cache_File.php +455 -0
  16. Cache_File_Cleaner.php +116 -0
  17. Cache_File_Cleaner_Generic.php +117 -0
  18. Cache_File_Generic.php +285 -0
  19. Cache_Memcache.php +315 -0
  20. Cache_Memcached.php +387 -0
  21. Cache_Memcached_Stats.php +87 -0
  22. Cache_Redis.php +353 -0
  23. Cache_Wincache.php +233 -0
  24. Cache_Xcache.php +237 -0
  25. CdnEngine.php +100 -0
  26. CdnEngine_Azure.php +488 -0
  27. CdnEngine_Base.php +705 -0
  28. CdnEngine_Ftp.php +427 -0
  29. CdnEngine_GoogleDrive.php +512 -0
  30. CdnEngine_Mirror.php +97 -0
  31. CdnEngine_Mirror_Akamai.php +113 -0
  32. CdnEngine_Mirror_Att.php +11 -0
  33. CdnEngine_Mirror_Cotendo.php +124 -0
  34. CdnEngine_Mirror_Edgecast.php +146 -0
  35. CdnEngine_Mirror_Highwinds.php +169 -0
  36. CdnEngine_Mirror_MaxCdn.php +7 -0
  37. CdnEngine_Mirror_Netdna.php +178 -0
  38. CdnEngine_Mirror_RackSpaceCdn.php +167 -0
  39. CdnEngine_RackSpaceCloudFiles.php +331 -0
  40. CdnEngine_S3.php +466 -0
  41. CdnEngine_S3_Cf.php +440 -0
  42. CdnEngine_S3_Cf_Custom.php +19 -0
  43. CdnEngine_S3_Cf_S3.php +10 -0
  44. CdnEngine_S3_Compatible.php +345 -0
  45. Cdn_AdminActions.php +865 -0
  46. Cdn_AdminNotes.php +326 -0
  47. Cdn_CacheFlush.php +73 -0
  48. Cdn_CloudFrontFsd_Api.php +339 -0
  49. Cdn_CloudFrontFsd_Engine.php +54 -0
  50. Cdn_CloudFrontFsd_Page.php +18 -0
  51. Cdn_CloudFrontFsd_Page_View.js +86 -0
  52. Cdn_CloudFrontFsd_Page_View.php +69 -0
  53. Cdn_CloudFrontFsd_Popup.php +345 -0
  54. Cdn_CloudFrontFsd_Popup_View_Distribution.php +60 -0
  55. Cdn_CloudFrontFsd_Popup_View_Distributions.php +54 -0
  56. Cdn_CloudFrontFsd_Popup_View_Intro.php +41 -0
  57. Cdn_CloudFrontFsd_Popup_View_Success.php +25 -0
  58. Cdn_ConfigLabels.php +34 -0
  59. Cdn_Core.php +725 -0
  60. Cdn_Core_Admin.php +805 -0
  61. Cdn_Environment.php +332 -0
  62. Cdn_Fsd_CacheFlush.php +235 -0
  63. Cdn_Fsd_Core.php +41 -0
  64. Cdn_Fsd_Util.php +16 -0
  65. Cdn_GeneralPage_View.php +46 -0
  66. Cdn_GoogleDrive_AdminActions.php +79 -0
  67. Cdn_GoogleDrive_Page.php +41 -0
  68. Cdn_GoogleDrive_Page_View.js +17 -0
  69. Cdn_GoogleDrive_Page_View.php +39 -0
  70. Cdn_GoogleDrive_Popup_AuthReturn.php +38 -0
  71. Cdn_GoogleDrive_Popup_AuthReturn_View.php +45 -0
  72. Cdn_Highwinds_Api.php +218 -0
  73. Cdn_Highwinds_Page.php +18 -0
  74. Cdn_Highwinds_Page_View.js +100 -0
  75. Cdn_Highwinds_Page_View.php +59 -0
  76. Cdn_Highwinds_Popup.php +271 -0
  77. Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php +25 -0
  78. Cdn_Highwinds_Popup_View_Intro.php +32 -0
  79. Cdn_Highwinds_Popup_View_SelectHost.php +50 -0
  80. Cdn_Highwinds_Widget.php +105 -0
  81. Cdn_Highwinds_Widget_View.css +15 -0
  82. Cdn_Highwinds_Widget_View.js +45 -0
  83. Cdn_Highwinds_Widget_View.php +42 -0
  84. Cdn_Highwinds_Widget_View_NotConfigured.php +10 -0
  85. Cdn_MaxCdnFsd_Engine.php +54 -0
  86. Cdn_MaxCdnFsd_Page.php +18 -0
  87. Cdn_MaxCdnFsd_Page_View.js +86 -0
  88. Cdn_MaxCdnFsd_Page_View.php +72 -0
  89. Cdn_MaxCdnFsd_Popup.php +287 -0
  90. Cdn_MaxCdnFsd_Popup_View_Intro.php +39 -0
  91. Cdn_MaxCdnFsd_Popup_View_Success.php +26 -0
  92. Cdn_MaxCdnFsd_Popup_View_Zone.php +55 -0
  93. Cdn_MaxCdnFsd_Popup_View_Zones.php +53 -0
  94. Cdn_Page.php +104 -0
  95. Cdn_Page_View_Fsd_HeaderActions.php +14 -0
  96. Cdn_Page_View_Header.php +16 -0
  97. Cdn_Plugin.php +1162 -0
  98. Cdn_Plugin_Admin.php +222 -0
  99. Cdn_Plugin_WidgetMaxCdn.php +190 -0
  100. Cdn_Plugin_WidgetNetDna.php +191 -0
  101. Cdn_RackSpaceCdn_AdminActions.php +29 -0
  102. Cdn_RackSpaceCdn_Page.php +36 -0
  103. Cdn_RackSpaceCdn_Page_View.js +159 -0
  104. Cdn_RackSpaceCdn_Page_View.php +83 -0
  105. Cdn_RackSpaceCdn_Popup.php +547 -0
  106. Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php +25 -0
  107. Cdn_RackSpaceCdn_Popup_View_Intro.php +42 -0
  108. Cdn_RackSpaceCdn_Popup_View_Regions.php +45 -0
  109. Cdn_RackSpaceCdn_Popup_View_Service_Actualize.php +48 -0
  110. Cdn_RackSpaceCdn_Popup_View_Service_Create.php +96 -0
  111. Cdn_RackSpaceCdn_Popup_View_Service_Created.php +57 -0
  112. Cdn_RackSpaceCdn_Popup_View_Services.php +53 -0
  113. Cdn_RackSpaceCloudFiles_Page.php +35 -0
  114. Cdn_RackSpaceCloudFiles_Page_View.js +28 -0
  115. Cdn_RackSpaceCloudFiles_Page_View.php +77 -0
  116. Cdn_RackSpaceCloudFiles_Popup.php +192 -0
  117. Cdn_RackSpaceCloudFiles_Popup_View_Containers.php +53 -0
  118. Cdn_RackSpaceCloudFiles_Popup_View_Intro.php +43 -0
  119. Cdn_RackSpaceCloudFiles_Popup_View_Regions.php +45 -0
  120. lib/CF/cacert.pem → Cdn_RackSpace_Api_CaCert.pem +0 -0
  121. Cdn_RackSpace_Api_Cdn.php +227 -0
  122. Cdn_RackSpace_Api_CloudFiles.php +150 -0
  123. Cdn_RackSpace_Api_CloudFilesCdn.php +143 -0
  124. Cdn_RackSpace_Api_Tokens.php +145 -0
  125. Cdn_Util.php +224 -0
  126. Cli.php +320 -0
  127. Config.php +373 -0
  128. ConfigCompiler.php +396 -0
  129. ConfigKeys.php +1917 -0
  130. ConfigState.php +191 -0
  131. ConfigStateNote.php +49 -0
  132. DbCache_ConfigLabels.php +18 -0
  133. DbCache_Core.php +39 -0
  134. DbCache_Environment.php +167 -0
  135. DbCache_Page.php +40 -0
  136. DbCache_Plugin.php +231 -0
  137. DbCache_Plugin_Admin.php +78 -0
  138. DbCache_Wpdb.php +464 -0
  139. DbCache_WpdbBase.php +7 -0
  140. DbCache_WpdbInjection.php +147 -0
  141. DbCache_WpdbInjection_QueryCaching.php +666 -0
  142. Dispatcher.php +308 -0
  143. Enterprise_CacheFlush_MakeSnsEvent.php +242 -0
  144. Enterprise_Dbcache_WpdbInjection_Cluster.php +813 -0
  145. Enterprise_SnsBase.php +74 -0
  146. Enterprise_SnsServer.php +137 -0
  147. Extension_CloudFlare_AdminActions.php +80 -0
  148. Extension_CloudFlare_Api.php +199 -0
  149. Extension_CloudFlare_GeneralPage_View.php +46 -0
  150. Extension_CloudFlare_Page.php +123 -0
  151. Extension_CloudFlare_Page_View.js +32 -0
  152. Extension_CloudFlare_Page_View.php +460 -0
  153. Extension_CloudFlare_Plugin.php +422 -0
  154. Extension_CloudFlare_Plugin_Admin.php +361 -0
  155. Extension_CloudFlare_Popup.php +120 -0
  156. Extension_CloudFlare_Popup_View_Intro.php +42 -0
  157. Extension_CloudFlare_Popup_View_Zones.php +43 -0
  158. Extension_CloudFlare_SettingsForUi.php +144 -0
  159. Extension_CloudFlare_View_Comments.css +17 -0
  160. Extension_CloudFlare_View_Comments.js +65 -0
  161. Extension_CloudFlare_View_Dashboard.js +15 -0
  162. Extension_CloudFlare_Widget.php +112 -0
  163. Extension_CloudFlare_Widget_View.css +32 -0
  164. Extension_CloudFlare_Widget_View.php +48 -0
  165. Extension_FeedBurner_Environment.php +197 -0
  166. Extension_FeedBurner_Page.php +11 -0
  167. Extension_FeedBurner_Page_View.php +37 -0
  168. Extension_FeedBurner_Plugin.php +84 -0
  169. Extension_FeedBurner_Plugin_Admin.php +55 -0
  170. Extension_FragmentCache_Api.php +192 -0
  171. Extension_FragmentCache_Core.php +91 -0
  172. Extension_FragmentCache_Environment.php +36 -0
  173. Extension_FragmentCache_GeneralPage.php +32 -0
  174. Extension_FragmentCache_GeneralPage_View.php +26 -0
  175. Extension_FragmentCache_Page.php +14 -0
  176. Extension_FragmentCache_Page_View.php +107 -0
  177. Extension_FragmentCache_Plugin.php +225 -0
  178. Extension_FragmentCache_Plugin_Admin.php +181 -0
  179. Extension_FragmentCache_WpObjectCache.php +661 -0
  180. Extension_Genesis_Page.php +11 -0
  181. Extension_Genesis_Page_View.php +243 -0
  182. Extension_Genesis_Plugin.php +403 -0
  183. Extension_Genesis_Plugin_Admin.php +137 -0
  184. Extension_NewRelic_AdminActions.php +25 -0
  185. Extension_NewRelic_AdminNotes.php +46 -0
  186. Extension_NewRelic_Api.php +112 -0
  187. Extension_NewRelic_Core.php +36 -0
  188. Extension_NewRelic_GeneralPage.php +42 -0
  189. Extension_NewRelic_GeneralPage_View.php +50 -0
  190. Extension_NewRelic_Page.php +41 -0
  191. Extension_NewRelic_Page_View_Apm.php +216 -0
  192. Extension_NewRelic_Plugin.php +196 -0
  193. Extension_NewRelic_Plugin_Admin.php +213 -0
  194. Extension_NewRelic_Popup.php +106 -0
  195. Extension_NewRelic_Popup_View.js +48 -0
  196. Extension_NewRelic_Popup_View_Intro.php +37 -0
  197. Extension_NewRelic_Popup_View_ListApplications.php +62 -0
  198. Extension_NewRelic_Service.php +632 -0
  199. Extension_NewRelic_Widget.php +293 -0
  200. Extension_NewRelic_Widget_View.css +77 -0
  201. Extension_NewRelic_Widget_View.js +70 -0
  202. Extension_NewRelic_Widget_View_Apm.php +68 -0
  203. Extension_NewRelic_Widget_View_Browser.php +13 -0
  204. Extension_NewRelic_Widget_View_NotConfigured.php +8 -0
  205. Extension_WordPressSeo_Plugin.php +44 -0
  206. Extension_WordPressSeo_Plugin_Admin.php +133 -0
  207. Extension_Wpml_Plugin.php +39 -0
  208. Extension_Wpml_Plugin_Admin.php +130 -0
  209. Extensions_AdminActions.php +28 -0
  210. Extensions_Page.php +78 -0
  211. Extensions_Plugin_Admin.php +179 -0
Base_Page_Settings.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Base_Page_Settings {
7
+ /**
8
+ * Config
9
+ *
10
+ * @var Config
11
+ */
12
+ protected $_config = null;
13
+
14
+ /**
15
+ * Notes
16
+ *
17
+ * @var array
18
+ */
19
+ protected $_notes = array();
20
+
21
+ /**
22
+ * Errors
23
+ *
24
+ * @var array
25
+ */
26
+ protected $_errors = array();
27
+
28
+ /**
29
+ * Used in PHPMailer init function
30
+ *
31
+ * @var string
32
+ */
33
+ protected $_phpmailer_sender = '';
34
+
35
+ /**
36
+ * Master configuration
37
+ *
38
+ * @var Config
39
+ */
40
+ protected $_config_master;
41
+
42
+ protected $_page;
43
+
44
+ function __construct() {
45
+ $this->_config = Dispatcher::config();
46
+ $this->_config_master = Dispatcher::config_master();
47
+
48
+ $this->_page = Util_Admin::get_current_page();
49
+ }
50
+
51
+ function options() {
52
+ $this->view();
53
+ }
54
+
55
+ public function render_footer() {
56
+ include W3TC_INC_OPTIONS_DIR . '/common/footer.php';
57
+ }
58
+
59
+ /**
60
+ * Returns true if config section is sealed
61
+ *
62
+ * @param string $section
63
+ * @return boolean
64
+ */
65
+ protected function is_sealed( $section ) {
66
+ return true;
67
+ }
68
+
69
+ /**
70
+ * Returns true if we edit master config
71
+ *
72
+ * @return boolean
73
+ */
74
+ protected function is_master() {
75
+ return $this->_config->is_master();
76
+ }
77
+
78
+ /**
79
+ * Prints checkbox with config option value
80
+ *
81
+ * @param string $option_id
82
+ * @param bool $disabled
83
+ * @param string $class_prefix
84
+ * @param bool $label
85
+ */
86
+ protected function checkbox( $option_id, $disabled = false,
87
+ $class_prefix = '', $label = true, $force_value = null ) {
88
+ $disabled = $disabled || $this->_config->is_sealed( $option_id );
89
+ $name = Util_Ui::config_key_to_http_name( $option_id );
90
+
91
+ if ( !$disabled )
92
+ echo '<input type="hidden" name="' . $name . '" value="0" />';
93
+
94
+ if ( $label )
95
+ echo '<label>';
96
+ echo '<input class="'.$class_prefix.'enabled" type="checkbox" id="' . $name .
97
+ '" name="' . $name . '" value="1" ';
98
+ if ( !is_null( $force_value ) )
99
+ checked( $force_value, true );
100
+ else
101
+ checked( $this->_config->get_boolean( $option_id ), true );
102
+
103
+ if ( $disabled )
104
+ echo 'disabled="disabled" ';
105
+
106
+ echo ' />';
107
+ }
108
+
109
+ /**
110
+ * Prints a radio button and if config value matches value
111
+ *
112
+ * @param string $option_id config id
113
+ * @param unknown $value
114
+ * @param bool $disabled
115
+ * @param string $class_prefix
116
+ */
117
+ protected function radio( $option_id, $value, $disabled = false, $class_prefix = '' ) {
118
+ if ( is_bool( $value ) )
119
+ $rValue = $value?'1':'0';
120
+ else
121
+ $rValue = $value;
122
+ $disabled = $disabled || $this->_config->is_sealed( $option_id );
123
+
124
+ $name = Util_Ui::config_key_to_http_name( $option_id );
125
+
126
+ echo '<label>';
127
+ echo '<input class="'.$class_prefix.'enabled" type="radio" id="' . $name .
128
+ '" name="' . $name . '" value="', $rValue, '" ';
129
+ checked( $this->_config->get_boolean( $option_id ), $value );
130
+
131
+ if ( $disabled )
132
+ echo 'disabled="disabled" ';
133
+
134
+ echo ' />';
135
+ }
136
+
137
+ /**
138
+ * Prints checkbox for debug option
139
+ *
140
+ * @param string $option_id
141
+ */
142
+ protected function checkbox_debug( $option_id ) {
143
+ if ( is_array( $option_id ) ) {
144
+ $section = $option_id[0];
145
+ $section_enabled = $this->_config->is_extension_active_frontend( $section );
146
+ } else {
147
+ $section = substr( $option_id, 0, strrpos( $option_id, '.' ) );
148
+ $section_enabled = $this->_config->get_boolean( $section . '.enabled' );
149
+ }
150
+
151
+ $disabled = $this->_config->is_sealed( $option_id ) || !$section_enabled;
152
+ $name = Util_Ui::config_key_to_http_name( $option_id );
153
+
154
+ if ( !$disabled )
155
+ echo '<input type="hidden" name="' . $name . '" value="0" />';
156
+
157
+ echo '<label>';
158
+ echo '<input class="enabled" type="checkbox" name="' . $name .
159
+ '" value="1" ';
160
+ checked( $this->_config->get_boolean( $option_id ), true );
161
+
162
+ if ( $disabled )
163
+ echo 'disabled="disabled" ';
164
+
165
+ echo ' />';
166
+ }
167
+
168
+ protected function value_with_disabled( $option_id, $disabled,
169
+ $value_when_disabled ) {
170
+ if ( $disabled ) {
171
+ echo 'value="' . esc_attr( $value_when_disabled ) . '" ';
172
+ echo 'disabled="disabled" ';
173
+ } else {
174
+ echo 'value="' .
175
+ esc_attr( $this->_config->get_string( $option_id ) ) . '" ';
176
+ }
177
+ }
178
+
179
+ protected function view() {
180
+ include W3TC_INC_DIR . '/options/common/header.php';
181
+ }
182
+ }
BrowserCache_ConfigLabels.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class BrowserCache_ConfigLabels {
5
+ public function config_labels( $config_labels ) {
6
+ return array_merge( $config_labels, array(
7
+ 'browsercache.enabled' => __( 'Browser Cache:', 'w3-total-cache' ),
8
+ 'browsercache.replace.exceptions' => __( 'Prevent caching exception list:', 'w3-total-cache' ),
9
+ 'browsercache.no404wp' => __( 'Do not process 404 errors for static objects with WordPress', 'w3-total-cache' ),
10
+ 'browsercache.no404wp.exceptions' => __( '404 error exception list:', 'w3-total-cache' ),
11
+ 'browsercache.cssjs.last_modified' => __( 'Set Last-Modified header', 'w3-total-cache' ),
12
+ 'browsercache.cssjs.expires' => __( 'Set expires header', 'w3-total-cache' ),
13
+ 'browsercache.cssjs.lifetime' => __( 'Expires header lifetime:', 'w3-total-cache' ),
14
+ 'browsercache.cssjs.cache.control' => __( 'Set cache control header', 'w3-total-cache' ),
15
+ 'browsercache.cssjs.cache.policy' => __( 'Cache Control policy:', 'w3-total-cache' ),
16
+ 'browsercache.cssjs.etag' => __( 'Set entity tag (ETag)', 'w3-total-cache' ),
17
+ 'browsercache.cssjs.w3tc' => __( 'Set W3 Total Cache header', 'w3-total-cache' ),
18
+ 'browsercache.cssjs.compression' => __( 'Enable <acronym title="Hypertext Transfer Protocol">HTTP</acronym> (gzip) compression', 'w3-total-cache' ),
19
+ 'browsercache.cssjs.replace' => __( 'Prevent caching of objects after settings change', 'w3-total-cache' ),
20
+ 'browsercache.cssjs.nocookies' => __( 'Disable cookies for static files', 'w3-total-cache' ),
21
+ 'browsercache.html.last_modified' => __( 'Set Last-Modified header', 'w3-total-cache' ),
22
+ 'browsercache.html.expires' => __( 'Set expires header', 'w3-total-cache' ),
23
+ 'browsercache.html.lifetime' => __( 'Expires header lifetime:', 'w3-total-cache' ),
24
+ 'browsercache.html.cache.control' => __( 'Set cache control header', 'w3-total-cache' ),
25
+ 'browsercache.html.cache.policy' => __( 'Cache Control policy:', 'w3-total-cache' ),
26
+ 'browsercache.html.etag' => __( 'Set entity tag (ETag)', 'w3-total-cache' ),
27
+ 'browsercache.html.w3tc' => __( 'Set W3 Total Cache header', 'w3-total-cache' ),
28
+ 'browsercache.html.compression' => __( 'Enable <acronym title="Hypertext Transfer Protocol">HTTP</acronym> (gzip) compression', 'w3-total-cache' ),
29
+ 'browsercache.other.last_modified' => __( 'Set Last-Modified header', 'w3-total-cache' ),
30
+ 'browsercache.other.expires' => __( 'Set expires header', 'w3-total-cache' ),
31
+ 'browsercache.other.lifetime' => __( 'Expires header lifetime:', 'w3-total-cache' ),
32
+ 'browsercache.other.cache.control' => __( 'Set cache control header', 'w3-total-cache' ),
33
+ 'browsercache.other.cache.policy' => __( 'Cache Control policy:', 'w3-total-cache' ),
34
+ 'browsercache.other.etag' => __( 'Set entity tag (ETag)', 'w3-total-cache' ),
35
+ 'browsercache.other.w3tc' => __( 'Set W3 Total Cache header', 'w3-total-cache' ),
36
+ 'browsercache.other.compression' => __( 'Enable <acronym title="Hypertext Transfer Protocol">HTTP</acronym> (gzip) compression</label>', 'w3-total-cache' ),
37
+ 'browsercache.other.replace' => __( 'Prevent caching of objects after settings change', 'w3-total-cache' ),
38
+ 'browsercache.other.nocookies' => __( 'Disable cookies for static files', 'w3-total-cache' )
39
+ ) );
40
+ }
41
+ }
BrowserCache_Core.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Browsercache core
6
+ */
7
+ class BrowserCache_Core {
8
+ /**
9
+ * Returns replace extensions
10
+ *
11
+ * @return array
12
+ */
13
+ public function get_replace_extensions( $config ) {
14
+ $types = array();
15
+ $extensions = array();
16
+
17
+ if ( $config->get_boolean( 'browsercache.cssjs.replace' ) ) {
18
+ $types = array_merge( $types, array_keys( $this->_get_cssjs_types() ) );
19
+ }
20
+
21
+ if ( $config->get_boolean( 'browsercache.html.replace' ) ) {
22
+ $types = array_merge( $types, array_keys( $this->_get_html_types() ) );
23
+ }
24
+
25
+ if ( $config->get_boolean( 'browsercache.other.replace' ) ) {
26
+ $types = array_merge( $types, array_keys( $this->_get_other_types() ) );
27
+ }
28
+
29
+ foreach ( $types as $type ) {
30
+ $extensions = array_merge( $extensions, explode( '|', $type ) );
31
+ }
32
+
33
+ return $extensions;
34
+ }
35
+
36
+
37
+
38
+ /**
39
+ * Returns CSS/JS mime types
40
+ *
41
+ * @return array
42
+ */
43
+ private function _get_cssjs_types() {
44
+ $mime_types = include W3TC_INC_DIR . '/mime/cssjs.php';
45
+ return $mime_types;
46
+ }
47
+
48
+
49
+
50
+ /**
51
+ * Returns HTML mime types
52
+ *
53
+ * @return array
54
+ */
55
+ private function _get_html_types() {
56
+ $mime_types = include W3TC_INC_DIR . '/mime/html.php';
57
+ return $mime_types;
58
+ }
59
+
60
+
61
+
62
+ /**
63
+ * Returns other mime types
64
+ *
65
+ * @return array
66
+ */
67
+ private function _get_other_types() {
68
+ $mime_types = include W3TC_INC_DIR . '/mime/other.php';
69
+ return $mime_types;
70
+ }
71
+ }
BrowserCache_Environment.php ADDED
@@ -0,0 +1,830 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class BrowserCache_Environment
6
+ */
7
+ class BrowserCache_Environment {
8
+ /**
9
+ * Fixes environment in each wp-admin request
10
+ *
11
+ * @param Config $config
12
+ * @param bool $force_all_checks
13
+ * @throws Util_Environment_Exceptions
14
+ */
15
+ public function fix_on_wpadmin_request( $config, $force_all_checks ) {
16
+ $exs = new Util_Environment_Exceptions();
17
+
18
+ if ( $config->get_boolean( 'config.check' ) || $force_all_checks ) {
19
+ if ( $config->get_boolean( 'browsercache.enabled' ) ) {
20
+ $this->rules_cache_add( $config, $exs );
21
+ } else {
22
+ $this->rules_cache_remove( $exs );
23
+ }
24
+
25
+ if ( $config->get_boolean( 'browsercache.enabled' ) &&
26
+ $config->get_boolean( 'browsercache.no404wp' ) ) {
27
+ $this->rules_no404wp_add( $config, $exs );
28
+ } else {
29
+ $this->rules_no404wp_remove( $exs );
30
+ }
31
+ }
32
+
33
+ if ( count( $exs->exceptions() ) > 0 )
34
+ throw $exs;
35
+ }
36
+
37
+ /**
38
+ * Fixes environment once event occurs
39
+ *
40
+ * @throws Util_Environment_Exceptions
41
+ */
42
+ public function fix_on_event( $config, $event, $old_config = null ) {
43
+ }
44
+
45
+ /**
46
+ * Fixes environment after plugin deactivation
47
+ *
48
+ * @throws Util_Environment_Exceptions
49
+ */
50
+ public function fix_after_deactivation() {
51
+ $exs = new Util_Environment_Exceptions();
52
+
53
+ $this->rules_cache_remove( $exs );
54
+ $this->rules_no404wp_remove( $exs );
55
+
56
+ if ( count( $exs->exceptions() ) > 0 )
57
+ throw $exs;
58
+ }
59
+
60
+ /**
61
+ * Returns required rules for module
62
+ *
63
+ * @param Config $config
64
+ * @return array
65
+ */
66
+ public function get_required_rules( $config ) {
67
+ if ( !$config->get_boolean( 'browsercache.enabled' ) )
68
+ return null;
69
+
70
+ $rewrite_rules = array();
71
+ if ( Dispatcher::should_browsercache_generate_rules_for_cdn( $config ) ) {
72
+ $domain = Dispatcher::get_cdn_domain();
73
+ $cdn_rules_path = sprintf( 'ftp://%s/%s', $domain,
74
+ Util_Rule::get_cdn_rules_path() );
75
+ $rewrite_rules[] = array(
76
+ 'filename' => $cdn_rules_path,
77
+ 'content' => $this->rules_cache_generate( $config, true )
78
+ );
79
+ }
80
+
81
+ $browsercache_rules_cache_path = Util_Rule::get_browsercache_rules_cache_path();
82
+ $rewrite_rules[] = array(
83
+ 'filename' => $browsercache_rules_cache_path,
84
+ 'content' => $this->rules_cache_generate( $config )
85
+ );
86
+
87
+ if ( $config->get_boolean( 'browsercache.no404wp' ) ) {
88
+ $browsercache_rules_no404wp_path =
89
+ Util_Rule::get_browsercache_rules_no404wp_path();
90
+ $rewrite_rules[] = array(
91
+ 'filename' => $browsercache_rules_no404wp_path,
92
+ 'content' => $this->rules_no404wp_generate( $config )
93
+ );
94
+ }
95
+ return $rewrite_rules;
96
+ }
97
+
98
+ /**
99
+ * Returns mime types
100
+ *
101
+ * @return array
102
+ */
103
+ public function get_mime_types() {
104
+ $a = array(
105
+ 'cssjs' => include W3TC_INC_DIR . '/mime/cssjs.php',
106
+ 'html' => include W3TC_INC_DIR . '/mime/html.php',
107
+ 'other' => include W3TC_INC_DIR . '/mime/other.php'
108
+ );
109
+
110
+ $other_compression = $a['other'];
111
+ unset( $other_compression['asf|asx|wax|wmv|wmx'] );
112
+ unset( $other_compression['avi'] );
113
+ unset( $other_compression['divx'] );
114
+ unset( $other_compression['gif'] );
115
+ unset( $other_compression['gz|gzip'] );
116
+ unset( $other_compression['jpg|jpeg|jpe'] );
117
+ unset( $other_compression['mid|midi'] );
118
+ unset( $other_compression['mov|qt'] );
119
+ unset( $other_compression['mp3|m4a'] );
120
+ unset( $other_compression['mp4|m4v'] );
121
+ unset( $other_compression['mpeg|mpg|mpe'] );
122
+ unset( $other_compression['png'] );
123
+ unset( $other_compression['ra|ram'] );
124
+ unset( $other_compression['tar'] );
125
+ unset( $other_compression['wma'] );
126
+ unset( $other_compression['zip'] );
127
+
128
+ $a['other_compression'] = $other_compression;
129
+
130
+ return $a;
131
+ }
132
+
133
+ /**
134
+ * Generate rules for FTP upload
135
+ *
136
+ * @param Config $config
137
+ * @return string
138
+ */
139
+ public function rules_cache_generate_for_ftp( $config ) {
140
+ return $this->rules_cache_generate( $config, true );
141
+ }
142
+
143
+
144
+
145
+ /*
146
+ * rules cache
147
+ */
148
+
149
+ /**
150
+ * Writes cache rules
151
+ *
152
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
153
+ */
154
+ private function rules_cache_add( $config, $exs ) {
155
+ Util_Rule::add_rules( $exs,
156
+ Util_Rule::get_browsercache_rules_cache_path(),
157
+ $this->rules_cache_generate( $config ),
158
+ W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE,
159
+ W3TC_MARKER_END_BROWSERCACHE_CACHE,
160
+ array(
161
+ W3TC_MARKER_BEGIN_MINIFY_CORE => 0,
162
+ W3TC_MARKER_BEGIN_PGCACHE_CORE => 0,
163
+ W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP => 0,
164
+ W3TC_MARKER_BEGIN_WORDPRESS => 0,
165
+ W3TC_MARKER_END_PGCACHE_CACHE => strlen( W3TC_MARKER_END_PGCACHE_CACHE ) + 1,
166
+ W3TC_MARKER_END_MINIFY_CACHE => strlen( W3TC_MARKER_END_MINIFY_CACHE ) + 1
167
+ )
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Removes cache directives
173
+ *
174
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
175
+ */
176
+ private function rules_cache_remove( $exs ) {
177
+ Util_Rule::remove_rules( $exs,
178
+ Util_Rule::get_browsercache_rules_cache_path(),
179
+ W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE,
180
+ W3TC_MARKER_END_BROWSERCACHE_CACHE );
181
+ }
182
+
183
+ /**
184
+ * Returns cache rules
185
+ *
186
+ * @param Config $config
187
+ * @param bool $cdnftp
188
+ * @return string
189
+ */
190
+ public function rules_cache_generate( $config, $cdnftp = false ) {
191
+ switch ( true ) {
192
+ case Util_Environment::is_apache():
193
+ case Util_Environment::is_litespeed():
194
+ return $this->rules_cache_generate_apache( $config );
195
+
196
+ case Util_Environment::is_nginx():
197
+ return $this->rules_cache_generate_nginx( $config, $cdnftp );
198
+ }
199
+ return '';
200
+ }
201
+
202
+ /**
203
+ * Returns cache rules
204
+ *
205
+ * @param Config $config
206
+ * @return string
207
+ */
208
+ private function rules_cache_generate_apache( $config ) {
209
+ $mime_types2 = $this->get_mime_types();
210
+ $cssjs_types = $mime_types2['cssjs'];
211
+ $cssjs_types = array_unique( $cssjs_types );
212
+ $html_types = $mime_types2['html'];
213
+ $other_types = $mime_types2['other'];
214
+ $other_compression_types = $mime_types2['other_compression'];
215
+
216
+ $cssjs_expires = $config->get_boolean( 'browsercache.cssjs.expires' );
217
+ $html_expires = $config->get_boolean( 'browsercache.html.expires' );
218
+ $other_expires = $config->get_boolean( 'browsercache.other.expires' );
219
+
220
+ $cssjs_lifetime = $config->get_integer( 'browsercache.cssjs.lifetime' );
221
+ $html_lifetime = $config->get_integer( 'browsercache.html.lifetime' );
222
+ $other_lifetime = $config->get_integer( 'browsercache.other.lifetime' );
223
+ $compatibility = $config->get_boolean( 'pgcache.compatibility' );
224
+
225
+ $mime_types = array();
226
+
227
+ if ( $cssjs_expires && $cssjs_lifetime ) {
228
+ $mime_types = array_merge( $mime_types, $cssjs_types );
229
+ }
230
+
231
+ if ( $html_expires && $html_lifetime ) {
232
+ $mime_types = array_merge( $mime_types, $html_types );
233
+ }
234
+
235
+ if ( $other_expires && $other_lifetime ) {
236
+ $mime_types = array_merge( $mime_types, $other_types );
237
+ }
238
+
239
+ $rules = '';
240
+ $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "\n";
241
+
242
+ if ( count( $mime_types ) ) {
243
+ $rules .= "<IfModule mod_mime.c>\n";
244
+
245
+ foreach ( $mime_types as $ext => $mime_type ) {
246
+ $extensions = explode( '|', $ext );
247
+
248
+ if ( !is_array( $mime_type ) )
249
+ $mime_type = (array)$mime_type;
250
+ foreach ( $mime_type as $mime_type2 ) {
251
+ $rules .= " AddType " . $mime_type2;
252
+ foreach ( $extensions as $extension ) {
253
+ $rules .= " ." . $extension;
254
+ }
255
+ $rules .= "\n";
256
+ }
257
+ }
258
+
259
+ $rules .= "</IfModule>\n";
260
+
261
+ $rules .= "<IfModule mod_expires.c>\n";
262
+ $rules .= " ExpiresActive On\n";
263
+
264
+ if ( $cssjs_expires && $cssjs_lifetime ) {
265
+ foreach ( $cssjs_types as $mime_type ) {
266
+ $rules .= " ExpiresByType " . $mime_type . " A" . $cssjs_lifetime . "\n";
267
+ }
268
+ }
269
+
270
+ if ( $html_expires && $html_lifetime ) {
271
+ foreach ( $html_types as $mime_type ) {
272
+ $rules .= " ExpiresByType " . $mime_type . " A" . $html_lifetime . "\n";
273
+ }
274
+ }
275
+
276
+ if ( $other_expires && $other_lifetime ) {
277
+ foreach ( $other_types as $mime_type ) {
278
+ if ( is_array( $mime_type ) )
279
+ foreach ( $mime_type as $mime_type2 )
280
+ $rules .= " ExpiresByType " . $mime_type2 . " A" . $other_lifetime . "\n";
281
+ else
282
+ $rules .= " ExpiresByType " . $mime_type . " A" . $other_lifetime . "\n";
283
+ }
284
+ }
285
+
286
+ $rules .= "</IfModule>\n";
287
+ }
288
+
289
+ $cssjs_compression = $config->get_boolean( 'browsercache.cssjs.compression' );
290
+ $html_compression = $config->get_boolean( 'browsercache.html.compression' );
291
+ $other_compression = $config->get_boolean( 'browsercache.other.compression' );
292
+
293
+ if ( $cssjs_compression || $html_compression || $other_compression ) {
294
+ $compression_types = array();
295
+
296
+ if ( $cssjs_compression ) {
297
+ $compression_types = array_merge( $compression_types, $cssjs_types );
298
+ }
299
+
300
+ if ( $html_compression ) {
301
+ $compression_types = array_merge( $compression_types, $html_types );
302
+ }
303
+
304
+ if ( $other_compression ) {
305
+ $compression_types = array_merge( $compression_types,
306
+ $other_compression_types );
307
+ }
308
+
309
+ $rules .= "<IfModule mod_deflate.c>\n";
310
+ if ( $compatibility ) {
311
+ $rules .= " <IfModule mod_setenvif.c>\n";
312
+ $rules .= " BrowserMatch ^Mozilla/4 gzip-only-text/html\n";
313
+ $rules .= " BrowserMatch ^Mozilla/4\\.0[678] no-gzip\n";
314
+ $rules .= " BrowserMatch \\bMSIE !no-gzip !gzip-only-text/html\n";
315
+ $rules .= " BrowserMatch \\bMSI[E] !no-gzip !gzip-only-text/html\n";
316
+ $rules .= " </IfModule>\n";
317
+ }
318
+ $rules .= " <IfModule mod_headers.c>\n";
319
+ $rules .= " Header append Vary User-Agent env=!dont-vary\n";
320
+ $rules .= " </IfModule>\n";
321
+ if ( version_compare( $this->_get_server_version(), '2.3.7', '>=' ) ) {
322
+ $rules .= " <IfModule mod_filter.c>\n";
323
+ }
324
+ $rules .= " AddOutputFilterByType DEFLATE " . implode( ' ', $compression_types ) . "\n";
325
+ $rules .= " <IfModule mod_mime.c>\n";
326
+ $rules .= " # DEFLATE by extension\n";
327
+ $rules .= " AddOutputFilter DEFLATE js css htm html xml\n";
328
+ $rules .= " </IfModule>\n";
329
+
330
+ if ( version_compare( $this->_get_server_version(), '2.3.7', '>=' ) ) {
331
+ $rules .= " </IfModule>\n";
332
+ }
333
+ $rules .= "</IfModule>\n";
334
+ }
335
+
336
+ foreach ( $mime_types2 as $type => $extensions )
337
+ $rules .= $this->_rules_cache_generate_apache_for_type( $config,
338
+ $extensions, $type );
339
+
340
+ if ( $config->get_boolean( 'browsercache.hsts' ) ) {
341
+ $lifetime = $config->get_integer( 'browsercache.other.lifetime' );
342
+ $rules .= "<IfModule mod_headers.c>\n";
343
+ $rules .= " Header set strict-transport-security \"max-age=$lifetime\"\n";
344
+ $rules .= "</IfModule>\n";
345
+ }
346
+
347
+ if ( $config->get_boolean( 'browsercache.rewrite' ) ) {
348
+ $core = Dispatcher::component( 'BrowserCache_Core' );
349
+ $extensions = $core->get_replace_extensions( $config );
350
+
351
+ $rules .= "<IfModule mod_rewrite.c>\n";
352
+ $rules .= " RewriteCond %{REQUEST_FILENAME} !-f\n";
353
+ $rules .= ' RewriteRule ^(.+)\.(x[0-9]{5})\.(' .
354
+ implode( '|', $extensions ) . ')$ $1.$3 [L]' . "\n";
355
+ $rules .= "</IfModule>\n";
356
+ }
357
+
358
+ $rules .= W3TC_MARKER_END_BROWSERCACHE_CACHE . "\n";
359
+
360
+ return $rules;
361
+ }
362
+
363
+ /**
364
+ * Writes cache rules
365
+ *
366
+ * @param Config $config
367
+ * @param array $mime_types
368
+ * @param string $section
369
+ * @return string
370
+ */
371
+ private function _rules_cache_generate_apache_for_type( $config, $mime_types,
372
+ $section ) {
373
+ $is_disc_enhanced = $config->get_boolean( 'pgcache.enabled' ) &&
374
+ $config->get_string( 'pgcache.engine' ) == 'file_generic';
375
+ $cache_control = $config->get_boolean( 'browsercache.' . $section . '.cache.control' );
376
+ $etag = $config->get_boolean( 'browsercache.' . $section . '.etag' );
377
+ $w3tc = $config->get_boolean( 'browsercache.' . $section . '.w3tc' );
378
+ $unset_setcookie = $config->get_boolean( 'browsercache.' . $section . '.nocookies' );
379
+ $set_last_modified = $config->get_boolean( 'browsercache.' . $section . '.last_modified' );
380
+ $compatibility = $config->get_boolean( 'pgcache.compatibility' );
381
+
382
+ $extensions = array_keys( $mime_types );
383
+
384
+ // Remove ext from filesmatch if its the same as permalink extension
385
+ $pext = strtolower( pathinfo( get_option( 'permalink_structure' ), PATHINFO_EXTENSION ) );
386
+ if ( $pext ) {
387
+ $extensions = $this->_remove_extension_from_list( $extensions, $pext );
388
+ }
389
+
390
+ $extensions_lowercase = array_map( 'strtolower', $extensions );
391
+ $extensions_uppercase = array_map( 'strtoupper', $extensions );
392
+
393
+ $rules = '';
394
+ $headers_rules = '';
395
+
396
+ if ( $cache_control ) {
397
+ $cache_policy = $config->get_string( 'browsercache.' . $section . '.cache.policy' );
398
+
399
+ switch ( $cache_policy ) {
400
+ case 'cache':
401
+ $headers_rules .= " Header set Pragma \"public\"\n";
402
+ $headers_rules .= " Header set Cache-Control \"public\"\n";
403
+ break;
404
+
405
+ case 'cache_public_maxage':
406
+ $expires = $config->get_boolean( 'browsercache.' . $section . '.expires' );
407
+ $lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
408
+
409
+ $headers_rules .= " Header set Pragma \"public\"\n";
410
+
411
+ if ( $expires )
412
+ $headers_rules .= " Header append Cache-Control \"public\"\n";
413
+ else
414
+ $headers_rules .= " Header set Cache-Control \"max-age=" . $lifetime . ", public\"\n";
415
+
416
+ break;
417
+
418
+ case 'cache_validation':
419
+ $headers_rules .= " Header set Pragma \"public\"\n";
420
+ $headers_rules .= " Header set Cache-Control \"public, must-revalidate, proxy-revalidate\"\n";
421
+ break;
422
+
423
+ case 'cache_noproxy':
424
+ $headers_rules .= " Header set Pragma \"public\"\n";
425
+ $headers_rules .= " Header set Cache-Control \"private, must-revalidate\"\n";
426
+ break;
427
+
428
+ case 'cache_maxage':
429
+ $expires = $config->get_boolean( 'browsercache.' . $section . '.expires' );
430
+ $lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
431
+
432
+ $headers_rules .= " Header set Pragma \"public\"\n";
433
+
434
+ if ( $expires )
435
+ $headers_rules .= " Header append Cache-Control \"public, must-revalidate, proxy-revalidate\"\n";
436
+ else
437
+ $headers_rules .= " Header set Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\"\n";
438
+
439
+ break;
440
+
441
+ case 'no_cache':
442
+ $headers_rules .= " Header set Pragma \"no-cache\"\n";
443
+ $headers_rules .= " Header set Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\"\n";
444
+ break;
445
+ }
446
+ }
447
+
448
+ if ( $etag ) {
449
+ $rules .= " FileETag MTime Size\n";
450
+ } else {
451
+ if ( $compatibility ) {
452
+ $rules .= " FileETag None\n";
453
+ $headers_rules .= " Header unset ETag\n";
454
+ }
455
+ }
456
+
457
+ if ( $unset_setcookie )
458
+ $headers_rules .= " Header unset Set-Cookie\n";
459
+
460
+ if ( !$set_last_modified )
461
+ $headers_rules .= " Header unset Last-Modified\n";
462
+
463
+ if ( $w3tc )
464
+ $headers_rules .= " Header set X-Powered-By \"" .
465
+ Util_Environment::w3tc_header( $config ) . "\"\n";
466
+
467
+ if ( strlen( $headers_rules ) > 0 ) {
468
+ $rules .= " <IfModule mod_headers.c>\n";
469
+ $rules .= $headers_rules;
470
+ $rules .= " </IfModule>\n";
471
+ }
472
+
473
+ if ( strlen( $rules ) > 0 ) {
474
+ $rules = "<FilesMatch \"\\.(" . implode( '|',
475
+ array_merge( $extensions_lowercase, $extensions_uppercase ) ) .
476
+ ")$\">\n" . $rules;
477
+ $rules .= "</FilesMatch>\n";
478
+ }
479
+
480
+ return $rules;
481
+ }
482
+
483
+ /**
484
+ * Returns cache rules
485
+ *
486
+ * @param Config $config
487
+ * @param bool $cdnftp
488
+ * @return string
489
+ */
490
+ private function rules_cache_generate_nginx( $config, $cdnftp = false ) {
491
+ $mime_types = $this->get_mime_types();
492
+ $cssjs_types = $mime_types['cssjs'];
493
+ $cssjs_types = array_unique( $cssjs_types );
494
+ $html_types = $mime_types['html'];
495
+ $other_types = $mime_types['other'];
496
+ $other_compression_types = $mime_types['other_compression'];
497
+
498
+ $rules = '';
499
+ $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "\n";
500
+
501
+ if ( $config->get_boolean( 'browsercache.rewrite' ) ) {
502
+ $core = Dispatcher::component( 'BrowserCache_Core' );
503
+ $extensions = $core->get_replace_extensions( $config );
504
+
505
+ $rules .= 'location ~ "^(?<w3tcbc_base>.+)\.(x[0-9]{5})' .
506
+ '(?<w3tcbc_ext>\.(' . implode( '|', $extensions ) . '))$" {' . "\n";
507
+ $rules .= ' if (-f $document_root$w3tcbc_base$w3tcbc_ext) {' . "\n";
508
+ $rules .= ' rewrite .* $w3tcbc_base$w3tcbc_ext;' . "\n";
509
+ $rules .= " }\n";
510
+ $rules .= "}\n";
511
+
512
+ }
513
+
514
+ $cssjs_compression = $config->get_boolean( 'browsercache.cssjs.compression' );
515
+ $html_compression = $config->get_boolean( 'browsercache.html.compression' );
516
+ $other_compression = $config->get_boolean( 'browsercache.other.compression' );
517
+
518
+ if ( $cssjs_compression || $html_compression || $other_compression ) {
519
+ $compression_types = array();
520
+
521
+ if ( $cssjs_compression ) {
522
+ $compression_types = array_merge( $compression_types, $cssjs_types );
523
+ }
524
+
525
+ if ( $html_compression ) {
526
+ $compression_types = array_merge( $compression_types, $html_types );
527
+ }
528
+
529
+ if ( $other_compression ) {
530
+ $compression_types = array_merge( $compression_types,
531
+ $other_compression_types );
532
+ }
533
+
534
+ unset( $compression_types['html|htm'] );
535
+
536
+ // some nginx cant handle values longer than 47 chars
537
+ unset( $compression_types['odp'] );
538
+
539
+ $rules .= "gzip on;\n";
540
+ $rules .= "gzip_types " .
541
+ implode( ' ', array_unique( $compression_types ) ) . ";\n";
542
+ }
543
+
544
+ if ( $config->get_boolean( 'browsercache.no404wp' ) ) {
545
+ $exceptions = $config->get_array( 'browsercache.no404wp.exceptions' );
546
+
547
+ $impoloded = implode( '|', $exceptions );
548
+ if ( !empty( $impoloded ) ) {
549
+ $wp_uri = network_home_url( '', 'relative' );
550
+ $wp_uri = rtrim( $wp_uri, '/' );
551
+
552
+ $rules .= "location ~ (" . $impoloded . ") {\n";
553
+ $rules .= ' try_files $uri $uri/ $uri.html ' . $wp_uri .
554
+ '/index.php?$args;' . "\n";
555
+ $rules .= "}\n";
556
+ }
557
+ }
558
+
559
+ foreach ( $mime_types as $type => $extensions )
560
+ $this->_rules_cache_generate_nginx_for_type( $config, $rules,
561
+ $extensions, $type );
562
+
563
+ if ( $config->get_boolean( 'browsercache.hsts' ) ) {
564
+ $lifetime = $config->get_integer( 'browsercache.other.lifetime' );
565
+ $rules .= "add_header strict-transport-security \"max-age=$lifetime\";\n";
566
+ }
567
+
568
+ $rules .= W3TC_MARKER_END_BROWSERCACHE_CACHE . "\n";
569
+
570
+ return $rules;
571
+ }
572
+
573
+ /**
574
+ * Adds cache rules for type to &$rules
575
+ *
576
+ * @param Config $config
577
+ * @param string $rules
578
+ * @param array $mime_types
579
+ * @param string $section
580
+ * @return void
581
+ */
582
+ private function _rules_cache_generate_nginx_for_type( $config, &$rules,
583
+ $mime_types, $section ) {
584
+
585
+ $expires = $config->get_boolean( 'browsercache.' . $section . '.expires' );
586
+ $cache_control = $config->get_boolean( 'browsercache.' . $section . '.cache.control' );
587
+ $w3tc = $config->get_boolean( 'browsercache.' . $section . '.w3tc' );
588
+
589
+ if ( $expires || $cache_control || $w3tc ) {
590
+ $lifetime = $config->get_integer( 'browsercache.' . $section . '.lifetime' );
591
+
592
+ $extensions = array_keys( $mime_types );
593
+
594
+ // Remove ext from filesmatch if its the same as permalink extension
595
+ $pext = strtolower( pathinfo( get_option( 'permalink_structure' ), PATHINFO_EXTENSION ) );
596
+ if ( $pext ) {
597
+ $extensions = $this->_remove_extension_from_list( $extensions, $pext );
598
+ }
599
+
600
+ $rules .= "location ~ \\.(" . implode( '|', $extensions ) . ")$ {\n";
601
+
602
+ if ( $expires ) {
603
+ $rules .= " expires " . $lifetime . "s;\n";
604
+ }
605
+
606
+ $add_header_rules = '';
607
+
608
+ if ( $cache_control ) {
609
+ $cache_policy = $config->get_string( 'browsercache.' . $section . '.cache.policy' );
610
+
611
+ switch ( $cache_policy ) {
612
+ case 'cache':
613
+ $add_header_rules .= " add_header Pragma \"public\";\n";
614
+ $add_header_rules .= " add_header Cache-Control \"public\";\n";
615
+ break;
616
+
617
+ case 'cache_public_maxage':
618
+ $add_header_rules .= " add_header Pragma \"public\";\n";
619
+ $add_header_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public\";\n";
620
+ break;
621
+
622
+ case 'cache_validation':
623
+ $add_header_rules .= " add_header Pragma \"public\";\n";
624
+ $add_header_rules .= " add_header Cache-Control \"public, must-revalidate, proxy-revalidate\";\n";
625
+ break;
626
+
627
+ case 'cache_noproxy':
628
+ $add_header_rules .= " add_header Pragma \"public\";\n";
629
+ $add_header_rules .= " add_header Cache-Control \"private, must-revalidate\";\n";
630
+ break;
631
+
632
+ case 'cache_maxage':
633
+ $add_header_rules .= " add_header Pragma \"public\";\n";
634
+ $add_header_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\";\n";
635
+ break;
636
+
637
+ case 'no_cache':
638
+ $add_header_rules .= " add_header Pragma \"no-cache\";\n";
639
+ $add_header_rules .= " add_header Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\";\n";
640
+ break;
641
+ }
642
+ }
643
+
644
+ if ( $w3tc ) {
645
+ $add_header_rules .= " add_header X-Powered-By \"" .
646
+ Util_Environment::w3tc_header( $config ) . "\";\n";
647
+ }
648
+
649
+ $rules .= $add_header_rules;
650
+ $rules .= Dispatcher::on_browsercache_rules_generation_for_section(
651
+ $config, false, $section, $add_header_rules );
652
+
653
+ if ( !$config->get_boolean( 'browsercache.no404wp' ) ) {
654
+ $wp_uri = network_home_url( '', 'relative' );
655
+ $wp_uri = rtrim( $wp_uri, '/' );
656
+
657
+ $rules .= ' try_files $uri $uri/ $uri.html ' . $wp_uri .
658
+ '/index.php?$args;' . "\n";
659
+ }
660
+ $rules .= "}\n";
661
+ }
662
+ }
663
+
664
+
665
+
666
+ /*
667
+ * rules_no404wp
668
+ */
669
+
670
+ /**
671
+ * Writes no 404 by WP rules
672
+ *
673
+ * @param Config $config
674
+ * @param Util_Environment_Exceptions $exs
675
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form
676
+ */
677
+ private function rules_no404wp_add( $config, $exs ) {
678
+ Util_Rule::add_rules( $exs, Util_Rule::get_browsercache_rules_no404wp_path(),
679
+ $this->rules_no404wp_generate( $config ),
680
+ W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP,
681
+ W3TC_MARKER_END_BROWSERCACHE_NO404WP,
682
+ array(
683
+ W3TC_MARKER_BEGIN_WORDPRESS => 0,
684
+ W3TC_MARKER_END_PGCACHE_CORE =>
685
+ strlen( W3TC_MARKER_END_PGCACHE_CORE ) + 1,
686
+ W3TC_MARKER_END_MINIFY_CORE =>
687
+ strlen( W3TC_MARKER_END_MINIFY_CORE ) + 1,
688
+ W3TC_MARKER_END_BROWSERCACHE_CACHE =>
689
+ strlen( W3TC_MARKER_END_BROWSERCACHE_CACHE ) + 1,
690
+ W3TC_MARKER_END_PGCACHE_CACHE =>
691
+ strlen( W3TC_MARKER_END_PGCACHE_CACHE ) + 1,
692
+ W3TC_MARKER_END_MINIFY_CACHE =>
693
+ strlen( W3TC_MARKER_END_MINIFY_CACHE ) + 1
694
+ )
695
+ );
696
+ }
697
+
698
+ /**
699
+ * Removes 404 directives
700
+ *
701
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form
702
+ */
703
+ private function rules_no404wp_remove( $exs ) {
704
+ Util_Rule::remove_rules( $exs,
705
+ Util_Rule::get_browsercache_rules_no404wp_path(),
706
+ W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP,
707
+ W3TC_MARKER_END_BROWSERCACHE_NO404WP
708
+ );
709
+ }
710
+
711
+ /**
712
+ * Generate rules related to prevent for media 404 error by WP
713
+ *
714
+ * @param Config $config
715
+ * @return string
716
+ */
717
+ private function rules_no404wp_generate( $config ) {
718
+ switch ( true ) {
719
+ case Util_Environment::is_apache():
720
+ case Util_Environment::is_litespeed():
721
+ return $this->rules_no404wp_generate_apache( $config );
722
+ }
723
+
724
+ return false;
725
+ }
726
+
727
+ /**
728
+ * Generate rules related to prevent for media 404 error by WP
729
+ *
730
+ * @param Config $config
731
+ * @return string
732
+ */
733
+ private function rules_no404wp_generate_apache( $config ) {
734
+ $a = $this->get_mime_types();
735
+ $cssjs_types = $a['cssjs'];
736
+ $html_types = $a['html'];
737
+ $other_types = $a['other'];
738
+
739
+ $extensions = array_merge( array_keys( $cssjs_types ),
740
+ array_keys( $html_types ), array_keys( $other_types ) );
741
+
742
+ $permalink_structure = get_option( 'permalink_structure' );
743
+ $permalink_structure_ext = ltrim( strrchr( $permalink_structure, '.' ),
744
+ '.' );
745
+
746
+ if ( $permalink_structure_ext != '' ) {
747
+ foreach ( $extensions as $index => $extension ) {
748
+ if ( strstr( $extension, $permalink_structure_ext ) !== false ) {
749
+ $extensions[$index] = preg_replace( '~\|?' .
750
+ Util_Environment::preg_quote( $permalink_structure_ext ) .
751
+ '\|?~', '', $extension );
752
+ }
753
+ }
754
+ }
755
+
756
+ $exceptions = $config->get_array( 'browsercache.no404wp.exceptions' );
757
+ $wp_uri = network_home_url( '', 'relative' );
758
+ $wp_uri = rtrim( $wp_uri, '/' );
759
+
760
+ $rules = '';
761
+ $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP . "\n";
762
+ $rules .= "<IfModule mod_rewrite.c>\n";
763
+ $rules .= " RewriteEngine On\n";
764
+
765
+ // in subdir - rewrite theme files and similar to upper folder if file exists
766
+ if ( Util_Environment::is_wpmu() &&
767
+ !Util_Environment::is_wpmu_subdomain() ) {
768
+ $rules .= " RewriteCond %{REQUEST_FILENAME} !-f\n";
769
+ $rules .= " RewriteCond %{REQUEST_FILENAME} !-d\n";
770
+ $rules .= " RewriteCond %{REQUEST_URI} ^$wp_uri/([_0-9a-zA-Z-]+/)(.*\.)(" .
771
+ implode( '|', $extensions ) . ")$ [NC]\n";
772
+ $document_root = Util_Rule::apache_docroot_variable();
773
+ $rules .= ' RewriteCond "' . $document_root . $wp_uri .
774
+ '/%2%3" -f' . "\n";
775
+ $rules .= " RewriteRule .* $wp_uri/%2%3 [L]\n\n";
776
+ }
777
+
778
+
779
+ $rules .= " RewriteCond %{REQUEST_FILENAME} !-f\n";
780
+ $rules .= " RewriteCond %{REQUEST_FILENAME} !-d\n";
781
+
782
+ $imploded = implode( '|', $exceptions );
783
+ if ( !empty( $imploded ) )
784
+ $rules .= " RewriteCond %{REQUEST_URI} !(" . $imploded. ")\n";
785
+
786
+ $rules .= " RewriteCond %{REQUEST_URI} \\.(" .
787
+ implode( '|', $extensions ) . ")$ [NC]\n";
788
+ $rules .= " RewriteRule .* - [L]\n";
789
+ $rules .= "</IfModule>\n";
790
+ $rules .= W3TC_MARKER_END_BROWSERCACHE_NO404WP . "\n";
791
+
792
+ return $rules;
793
+ }
794
+
795
+ /**
796
+ * Returns the apache, nginx version
797
+ *
798
+ * @return string
799
+ */
800
+ private function _get_server_version() {
801
+ $sig= explode( '/', $_SERVER['SERVER_SOFTWARE'] );
802
+ $temp = isset( $sig[1] ) ? explode( ' ', $sig[1] ) : array( '0' );
803
+ $version = $temp[0];
804
+ return $version;
805
+ }
806
+
807
+ /**
808
+ * Takes an array of extensions single per row and/or extensions delimited by |
809
+ *
810
+ * @param unknown $extensions
811
+ * @param unknown $ext
812
+ * @return array
813
+ */
814
+ private function _remove_extension_from_list( $extensions, $ext ) {
815
+ for ( $i = 0; $i < sizeof( $extensions ); $i++ ) {
816
+ if ( $extensions[$i] == $ext ) {
817
+ unset( $extensions[$i] );
818
+ return $extensions;
819
+ } elseif ( strpos( $extensions[$i], $ext ) !== false &&
820
+ strpos( $extensions[$i], '|' ) !== false ) {
821
+ $exts = explode( '|', $extensions[$i] );
822
+ $key = array_search( $ext, $exts );
823
+ unset( $exts[$key] );
824
+ $extensions[$i] = implode( '|', $exts );
825
+ return $extensions;
826
+ }
827
+ }
828
+ return $extensions;
829
+ }
830
+ }
BrowserCache_Page.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class BrowserCache_Page extends Base_Page_Settings {
7
+ /**
8
+ * Current page
9
+ *
10
+ * @var string
11
+ */
12
+ protected $_page = 'w3tc_browsercache';
13
+
14
+
15
+ /**
16
+ * Browser cache tab
17
+ *
18
+ * @return void
19
+ */
20
+ function view() {
21
+ $browsercache_enabled = $this->_config->get_boolean( 'browsercache.enabled' );
22
+ $browsercache_last_modified = ( $this->_config->get_boolean( 'browsercache.cssjs.last_modified' ) && $this->_config->get_boolean( 'browsercache.html.last_modified' ) && $this->_config->get_boolean( 'browsercache.other.last_modified' ) );
23
+ $browsercache_expires = ( $this->_config->get_boolean( 'browsercache.cssjs.expires' ) && $this->_config->get_boolean( 'browsercache.html.expires' ) && $this->_config->get_boolean( 'browsercache.other.expires' ) );
24
+ $browsercache_cache_control = ( $this->_config->get_boolean( 'browsercache.cssjs.cache.control' ) && $this->_config->get_boolean( 'browsercache.html.cache.control' ) && $this->_config->get_boolean( 'browsercache.other.cache.control' ) );
25
+ $browsercache_etag = ( $this->_config->get_boolean( 'browsercache.cssjs.etag' ) && $this->_config->get_boolean( 'browsercache.html.etag' ) && $this->_config->get_boolean( 'browsercache.other.etag' ) );
26
+ $browsercache_w3tc = ( $this->_config->get_boolean( 'browsercache.cssjs.w3tc' ) && $this->_config->get_boolean( 'browsercache.html.w3tc' ) && $this->_config->get_boolean( 'browsercache.other.w3tc' ) );
27
+ $browsercache_compression = ( $this->_config->get_boolean( 'browsercache.cssjs.compression' ) && $this->_config->get_boolean( 'browsercache.html.compression' ) && $this->_config->get_boolean( 'browsercache.other.compression' ) );
28
+ $browsercache_replace = ( $this->_config->get_boolean( 'browsercache.cssjs.replace' ) && $this->_config->get_boolean( 'browsercache.other.replace' ) );
29
+ $browsercache_update_media_qs = ( $this->_config->get_boolean( 'browsercache.cssjs.replace' ) || $this->_config->get_boolean( 'browsercache.other.replace' ) );
30
+ $browsercache_nocookies =
31
+ ( $this->_config->get_boolean( 'browsercache.cssjs.nocookies' ) &&
32
+ $this->_config->get_boolean( 'browsercache.other.nocookies' ) );
33
+
34
+ include W3TC_INC_DIR . '/options/browsercache.php';
35
+ }
36
+ }
BrowserCache_Plugin.php ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 ObjectCache plugin
6
+ */
7
+ class BrowserCache_Plugin {
8
+ private $_config = null;
9
+ private $browsercache_rewrite;
10
+
11
+ function __construct() {
12
+ $this->_config = Dispatcher::config();
13
+ }
14
+
15
+ /**
16
+ * Runs plugin
17
+ */
18
+ function run() {
19
+ add_filter( 'w3tc_admin_bar_menu',
20
+ array( $this, 'w3tc_admin_bar_menu' ) );
21
+
22
+ if ( $this->_config->get_boolean( 'browsercache.html.w3tc' ) ) {
23
+ add_action( 'send_headers',
24
+ array( $this, 'send_headers' ) );
25
+ }
26
+
27
+ if ( !$this->_config->get_boolean( 'browsercache.html.etag' ) ) {
28
+ add_filter( 'wp_headers',
29
+ array( $this, 'filter_wp_headers' ),
30
+ 0, 2 );
31
+ }
32
+
33
+ if ( $this->can_ob() ) {
34
+ $this->browsercache_rewrite =
35
+ $this->_config->get_boolean( 'browsercache.rewrite' );
36
+ Util_Bus::add_ob_callback( 'browsercache', array( $this, 'ob_callback' ) );
37
+
38
+ // modify CDN urls too
39
+ add_filter( 'w3tc_cdn_url',
40
+ array( $this, 'w3tc_cdn_url' ),
41
+ 0, 3 );
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Check if we can start OB
47
+ *
48
+ * @return boolean
49
+ */
50
+ function can_ob() {
51
+ /**
52
+ * Replace feature should be enabled
53
+ */
54
+ if ( !$this->_config->get_boolean( 'browsercache.cssjs.replace' ) && !$this->_config->get_boolean( 'browsercache.html.replace' ) && !$this->_config->get_boolean( 'browsercache.other.replace' ) ) {
55
+ return false;
56
+ }
57
+
58
+ /**
59
+ * Skip if admin
60
+ */
61
+ if ( defined( 'WP_ADMIN' ) ) {
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Skip if doing AJAX
67
+ */
68
+ if ( defined( 'DOING_AJAX' ) ) {
69
+ return false;
70
+ }
71
+
72
+ /**
73
+ * Skip if doing cron
74
+ */
75
+ if ( defined( 'DOING_CRON' ) ) {
76
+ return false;
77
+ }
78
+
79
+ /**
80
+ * Skip if APP request
81
+ */
82
+ if ( defined( 'APP_REQUEST' ) ) {
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * Skip if XMLRPC request
88
+ */
89
+ if ( defined( 'XMLRPC_REQUEST' ) ) {
90
+ return false;
91
+ }
92
+
93
+ /**
94
+ * Check for WPMU's and WP's 3.0 short init
95
+ */
96
+ if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
97
+ return false;
98
+ }
99
+
100
+ /**
101
+ * Check User Agent
102
+ */
103
+ if ( isset( $_SERVER['HTTP_USER_AGENT'] ) &&
104
+ stristr( $_SERVER['HTTP_USER_AGENT'], W3TC_POWERED_BY ) !== false ) {
105
+ return false;
106
+ }
107
+
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * Output buffer callback
113
+ *
114
+ * @param string $buffer
115
+ * @return mixed
116
+ */
117
+ function ob_callback( $buffer ) {
118
+ if ( $buffer != '' && Util_Content::is_html( $buffer ) ) {
119
+ $domain_url_regexp = Util_Environment::home_domain_root_url_regexp();
120
+
121
+ $buffer = preg_replace_callback(
122
+ '~(href|src|action|extsrc|asyncsrc|w3tc_load_js\()=?([\'"])((' .
123
+ $domain_url_regexp .
124
+ ')?(/[^\'"]*\.([a-z-_]+)(\?[^\'"]*)?))[\'"]~Ui', array(
125
+ $this,
126
+ 'link_replace_callback'
127
+ ), $buffer );
128
+ }
129
+
130
+ return $buffer;
131
+ }
132
+
133
+ /**
134
+ * Link replace callback
135
+ *
136
+ * @param string $matches
137
+ * @return string
138
+ */
139
+ function link_replace_callback( $matches ) {
140
+ list ( $match, $attr, $quote, $url, , , , , $extension ) = $matches;
141
+
142
+ if ( !$this->_url_has_to_be_replaced( $url, $extension ) )
143
+ return $match;
144
+
145
+ $url = $this->mutate_url( $url, !$this->browsercache_rewrite );
146
+
147
+ if ( $attr != 'w3tc_load_js(' )
148
+ return $attr . '=' . $quote . $url . $quote;
149
+ return sprintf( '%s\'%s\'', $attr, $url );
150
+ }
151
+
152
+ /**
153
+ * Link replace for CDN url
154
+ *
155
+ * @param string $matches
156
+ * @return string
157
+ */
158
+ function w3tc_cdn_url( $url, $original_url, $is_cdn_mirror ) {
159
+ // decouple extension
160
+ $matches = array();
161
+ if ( !preg_match( '/\.([a-zA-Z0-9]+)($|[\?])/', $original_url, $matches ) )
162
+ return $url;
163
+ $extension = $matches[1];
164
+
165
+ if ( !$this->_url_has_to_be_replaced( $original_url, $extension ) )
166
+ return $url;
167
+
168
+ // for push cdns each flush would require manual reupload of files
169
+ $mutate_by_querystring = !$this->browsercache_rewrite || !$is_cdn_mirror;
170
+
171
+ $url = $this->mutate_url( $url, $mutate_by_querystring );
172
+ return $url;
173
+ }
174
+
175
+ private function mutate_url( $url, $mutate_by_querystring ) {
176
+ $id = $this->get_filename_uniqualizator();
177
+
178
+ $url = Util_Environment::remove_query( $url );
179
+ $query_pos = strpos( $url, '?' );
180
+
181
+ if ( $mutate_by_querystring ) {
182
+ $url .= ( $query_pos !== false ? '&amp;' : '?' ) . $id;
183
+ } else {
184
+ // add $id to url before extension
185
+
186
+ $url_query = '';
187
+ if ( $query_pos !== false ) {
188
+ $url_query = substr( $url, $query_pos );
189
+ $url = substr( $url, 0, $query_pos );
190
+ }
191
+
192
+ $ext_pos = strrpos( $url, '.' );
193
+ $extension = substr( $url, $ext_pos );
194
+
195
+ $url = substr( $url, 0, strlen( $url ) - strlen( $extension ) ) . '.' .
196
+ $id . $extension . $url_query;
197
+ }
198
+ return $url;
199
+ }
200
+
201
+ function _url_has_to_be_replaced( $url, $extension ) {
202
+ static $extensions = null;
203
+ if ( $extensions === null ) {
204
+ $core = Dispatcher::component( 'BrowserCache_Core' );
205
+ $extensions = $core->get_replace_extensions( $this->_config );
206
+ }
207
+
208
+ static $exceptions = null;
209
+ if ( $exceptions === null )
210
+ $exceptions = $this->_config->get_array( 'browsercache.replace.exceptions' );
211
+
212
+ if ( !in_array( $extension, $extensions ) )
213
+ return false;
214
+
215
+ $test_url = Util_Environment::remove_query( $url );
216
+ foreach ( $exceptions as $exception ) {
217
+ if ( trim( $exception ) && preg_match( '/' . $exception . '/', $test_url ) )
218
+ return false;
219
+ }
220
+
221
+ return true;
222
+ }
223
+
224
+ /**
225
+ * Returns replace ID
226
+ *
227
+ * @return string
228
+ */
229
+ function get_filename_uniqualizator() {
230
+ static $cache_id = null;
231
+
232
+ if ( $cache_id === null ) {
233
+ $value = get_option( 'w3tc_browsercache_flush_timestamp' );
234
+
235
+ if ( empty( $value ) ) {
236
+ $value = rand( 10000, 99999 ) . '';
237
+ update_option( 'w3tc_browsercache_flush_timestamp', $value );
238
+ }
239
+
240
+ $cache_id = substr( $value, 0, 5 );
241
+ }
242
+
243
+ return 'x' . $cache_id;
244
+ }
245
+
246
+ public function w3tc_admin_bar_menu( $menu_items ) {
247
+ $browsercache_update_media_qs =
248
+ ( $this->_config->get_boolean( 'browsercache.cssjs.replace' ) ||
249
+ $this->_config->get_boolean( 'browsercache.other.replace' ) );
250
+
251
+ if ( $browsercache_update_media_qs ) {
252
+ $menu_items['20190.browsercache'] = array(
253
+ 'id' => 'w3tc_flush_browsercache',
254
+ 'parent' => 'w3tc_flush',
255
+ 'title' => __( 'Browser Cache: Update Media Query String', 'w3-total-cache' ),
256
+ 'href' => wp_nonce_url( network_admin_url(
257
+ 'admin.php?page=w3tc_dashboard&amp;w3tc_flush_browser_cache' ),
258
+ 'w3tc' )
259
+ );
260
+ }
261
+
262
+ return $menu_items;
263
+ }
264
+
265
+ /**
266
+ * Send headers
267
+ */
268
+ function send_headers() {
269
+ @header( 'X-Powered-By: ' . Util_Environment::w3tc_header( $this->_config ) );
270
+ }
271
+
272
+ /**
273
+ * Returns cache config for CDN
274
+ *
275
+ * @return array
276
+ */
277
+ function get_cache_config() {
278
+ $config = array();
279
+
280
+ $e = Dispatcher::component( 'BrowserCache_Environment' );
281
+ $mime_types = $e->get_mime_types();
282
+
283
+ foreach ( $mime_types as $type => $extensions )
284
+ $this->_get_cache_config( $config, $extensions, $type );
285
+
286
+ return $config;
287
+ }
288
+
289
+ /**
290
+ * Writes cache config
291
+ *
292
+ * @param string $config
293
+ * @param array $mime_types
294
+ * @param array $section
295
+ * @return void
296
+ */
297
+ function _get_cache_config( &$config, $mime_types, $section ) {
298
+ $expires = $this->_config->get_boolean( 'browsercache.' . $section . '.expires' );
299
+ $lifetime = $this->_config->get_integer( 'browsercache.' . $section . '.lifetime' );
300
+ $cache_control = $this->_config->get_boolean( 'browsercache.' . $section . '.cache.control' );
301
+ $cache_policy = $this->_config->get_string( 'browsercache.' . $section . '.cache.policy' );
302
+ $etag = $this->_config->get_boolean( 'browsercache.' . $section . '.etag' );
303
+ $w3tc = $this->_config->get_boolean( 'browsercache.' . $section . '.w3tc' );
304
+
305
+ foreach ( $mime_types as $mime_type ) {
306
+ if ( is_array( $mime_type ) ) {
307
+ foreach ( $mime_type as $mime_type2 )
308
+ $config[$mime_type2] = array(
309
+ 'etag' => $etag,
310
+ 'w3tc' => $w3tc,
311
+ 'lifetime' => $lifetime,
312
+ 'expires' => $expires,
313
+ 'cache_control' => ( $cache_control ? $cache_policy : false )
314
+ );
315
+ } else
316
+ $config[$mime_type] = array(
317
+ 'etag' => $etag,
318
+ 'w3tc' => $w3tc,
319
+ 'lifetime' => $lifetime,
320
+ 'expires' => $expires,
321
+ 'cache_control' => ( $cache_control ? $cache_policy : false )
322
+ );
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Filters headers set by WordPress
328
+ *
329
+ * @param unknown $headers
330
+ * @param unknown $wp
331
+ * @return
332
+ */
333
+ function filter_wp_headers( $headers, $wp ) {
334
+ if ( !empty( $wp->query_vars['feed'] ) )
335
+ unset( $headers['ETag'] );
336
+ return $headers;
337
+ }
338
+ }
BrowserCache_Plugin_Admin.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class BrowserCache_Plugin_Admin {
5
+ function run() {
6
+ $config_labels = new BrowserCache_ConfigLabels();
7
+ add_filter( 'w3tc_config_labels', array(
8
+ $config_labels, 'config_labels' ) );
9
+ }
10
+ }
Cache.php ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Cache class
6
+ */
7
+
8
+ /**
9
+ * class Cache
10
+ */
11
+ class Cache {
12
+ /**
13
+ * Returns cache engine instance
14
+ *
15
+ * @param string $engine
16
+ * @param array $config
17
+ * @return W3_Cache_Base
18
+ */
19
+ static function instance( $engine, $config = array() ) {
20
+ static $instances = array();
21
+
22
+ // common configuration data
23
+ if ( !isset( $config['blog_id'] ) )
24
+ $config['blog_id'] = Util_Environment::blog_id();
25
+
26
+ $instance_key = sprintf( '%s_%s', $engine, md5( serialize( $config ) ) );
27
+
28
+ if ( !isset( $instances[$instance_key] ) ) {
29
+ switch ( $engine ) {
30
+ case 'apc':
31
+ if ( function_exists( 'apcu_store' ) )
32
+ $instances[$instance_key] = new Cache_Apcu( $config );
33
+ else if ( function_exists( 'apc_store' ) )
34
+ $instances[$instance_key] = new Cache_Apc( $config );
35
+ break;
36
+
37
+ case 'eaccelerator':
38
+ $instances[$instance_key] = new Cache_Eaccelerator( $config );
39
+ break;
40
+
41
+ case 'file':
42
+ $instances[$instance_key] = new Cache_File( $config );
43
+ break;
44
+
45
+ case 'file_generic':
46
+ $instances[$instance_key] = new Cache_File_Generic( $config );
47
+ break;
48
+
49
+ case 'memcached':
50
+ if ( class_exists( '\Memcached' ) ) {
51
+ $instances[$instance_key] = new Cache_Memcached( $config );
52
+ } else if ( class_exists( '\Memcache' ) ) {
53
+ $instances[$instance_key] = new Cache_Memcache( $config );
54
+ }
55
+ break;
56
+
57
+ case 'redis':
58
+ $instances[$instance_key] = new Cache_Redis( $config );
59
+ break;
60
+
61
+ case 'wincache':
62
+ $instances[$instance_key] = new Cache_Wincache( $config );
63
+ break;
64
+
65
+ case 'xcache':
66
+ $instances[$instance_key] = new Cache_Xcache( $config );
67
+ break;
68
+
69
+ default:
70
+ trigger_error( 'Incorrect cache engine ' . $engine, E_USER_WARNING );
71
+ $instances[$instance_key] = new Cache_Base( $config );
72
+ break;
73
+ }
74
+
75
+ if ( !isset( $instances[$instance_key] ) ||
76
+ !$instances[$instance_key]->available() ) {
77
+ $instances[$instance_key] = new Cache_Base( $config );
78
+ }
79
+ }
80
+
81
+ return $instances[$instance_key];
82
+ }
83
+
84
+ /**
85
+ * Returns caching engine name
86
+ *
87
+ * @param unknown $engine
88
+ * @param unknown $module
89
+ *
90
+ * @return string
91
+ */
92
+ static public function engine_name( $engine, $module = '' ) {
93
+ switch ( $engine ) {
94
+ case 'memcached':
95
+ if ( class_exists( 'Memcached' ) )
96
+ $engine_name = 'memcached';
97
+ else
98
+ $engine_name = 'memcache';
99
+
100
+ break;
101
+
102
+ case 'apc':
103
+ $engine_name = 'apc';
104
+ break;
105
+
106
+ case 'eaccelerator':
107
+ $engine_name = 'eaccelerator';
108
+ break;
109
+
110
+ case 'redis':
111
+ $engine_name = 'redis';
112
+ break;
113
+
114
+ case 'xcache':
115
+ $engine_name = 'xcache';
116
+ break;
117
+
118
+ case 'wincache':
119
+ $engine_name = 'wincache';
120
+ break;
121
+
122
+ case 'file':
123
+ if ( $module == 'pgcache' )
124
+ $engine_name = 'disk: basic';
125
+ else
126
+ $engine_name = 'disk';
127
+ break;
128
+
129
+ case 'file_generic':
130
+ $engine_name = 'disk: enhanced';
131
+ break;
132
+
133
+ case 'ftp':
134
+ $engine_name = 'self-hosted / file transfer protocol upload';
135
+ break;
136
+
137
+ case 's3':
138
+ $engine_name = 'amazon simple storage service (s3)';
139
+ break;
140
+
141
+ case 'cf':
142
+ $engine_name = 'amazon cloudfront';
143
+ break;
144
+
145
+ case 'google_drive':
146
+ $engine_name = 'google drive';
147
+ break;
148
+
149
+ case 'highwinds':
150
+ $engine_name = 'highwinds';
151
+ break;
152
+
153
+ case 'cf2':
154
+ $engine_name = 'amazon cloudfront';
155
+ break;
156
+
157
+ case 'rscf':
158
+ $engine_name = 'rackspace cloud files';
159
+ break;
160
+
161
+ case 'azure':
162
+ $engine_name = 'microsoft azure storage';
163
+ break;
164
+
165
+ case 'mirror':
166
+ $engine_name = 'mirror';
167
+ break;
168
+
169
+ case 'netdna':
170
+ $engine_name = 'netdna';
171
+ break;
172
+
173
+ case 'maxcdn':
174
+ $engine_name = 'maxcdn';
175
+ break;
176
+
177
+ case 'cotendo':
178
+ $engine_name = 'cotendo';
179
+ break;
180
+
181
+ case 'akamai':
182
+ $engine_name = 'akamai';
183
+ break;
184
+
185
+ case 'edgecast':
186
+ $engine_name = 'media template procdn / edgecast';
187
+ break;
188
+
189
+ case 'att':
190
+ $engine_name = 'at&amp;t';
191
+ break;
192
+
193
+ default:
194
+ $engine_name = 'n/a';
195
+ break;
196
+ }
197
+
198
+ return $engine_name;
199
+ }
200
+
201
+ }
CacheFlush.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Cache flushing
6
+ */
7
+ class CacheFlush {
8
+ private $_config;
9
+ /**
10
+ * PHP5 Constructor
11
+ */
12
+ function __construct() {
13
+ $this->_config = Dispatcher::config();
14
+ $sns = $this->_config->get_boolean( 'cluster.messagebus.enabled' );
15
+
16
+ if ( $sns )
17
+ $this->_executor = new Enterprise_CacheFlush_MakeSnsEvent();
18
+ else
19
+ $this->_executor = new CacheFlush_Locally();
20
+
21
+ if ( function_exists( 'add_action' ) ) {
22
+ add_action( 'w3tc_redirect', array(
23
+ $this,
24
+ 'execute_delayed_operations'
25
+ ), 100000, 0 );
26
+ add_filter( 'wp_redirect', array(
27
+ $this,
28
+ 'execute_delayed_operations_filter'
29
+ ), 100000, 1 );
30
+ add_action( 'w3tc_messagebus_message_processed', array(
31
+ $this,
32
+ 'execute_delayed_operations'
33
+ ), 0 );
34
+ add_action( 'shutdown', array(
35
+ $this,
36
+ 'execute_delayed_operations'
37
+ ), 100000, 0 );
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Flushes database cache
43
+ */
44
+ function dbcache_flush() {
45
+ if ( $this->_config->get_boolean( 'dbcache.enabled' ) ) {
46
+ $this->_executor->dbcache_flush();
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Flushes minify cache
52
+ */
53
+ function minifycache_flush() {
54
+ if ( $this->_config->get_boolean( 'minify.enabled' ) ) {
55
+ $this->_executor->minifycache_flush();
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Flushes object cache
61
+ */
62
+ function objectcache_flush() {
63
+ if ( $this->_config->get_boolean( 'objectcache.enabled' ) ) {
64
+ $this->_executor->objectcache_flush();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Flushes fragment cache
70
+ */
71
+ function fragmentcache_flush() {
72
+ $this->_executor->fragmentcache_flush();
73
+ }
74
+
75
+
76
+ /**
77
+ * Flushes fragment cache based on group
78
+ */
79
+ function fragmentcache_flush_group( $group ) {
80
+ $this->_executor->fragmentcache_flush_group( $group );
81
+ }
82
+
83
+ /**
84
+ * Updates Browser Query String
85
+ */
86
+ function browsercache_flush() {
87
+ if ( $this->_config->get_boolean( 'browsercache.enabled' ) ) {
88
+ $this->_executor->browsercache_flush();
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Purge CDN mirror cache
94
+ */
95
+ function cdn_purge_all() {
96
+ if ( $this->_config->get_boolean( 'cdn.enabled' ) )
97
+ return $this->_executor->cdn_purge_all();
98
+
99
+ return false;
100
+ }
101
+
102
+ /**
103
+ * Purges CDN files
104
+ */
105
+ function cdn_purge_files( $purgefiles ) {
106
+ $this->_executor->cdn_purge_files( $purgefiles );
107
+ }
108
+
109
+ /**
110
+ * Clears the system APC
111
+ *
112
+ * @return mixed
113
+ */
114
+ function opcache_flush() {
115
+ return $this->_executor->opcache_flush();
116
+ }
117
+
118
+ /**
119
+ * Reloads/compiles a PHP file.
120
+ *
121
+ * @param string $filename
122
+ * @return mixed
123
+ */
124
+ function opcache_flush_file( $filename ) {
125
+ return $this->_executor->opcache_flush_file( $filename );
126
+ }
127
+
128
+ /**
129
+ * Purges/Flushes post page
130
+ */
131
+ function flush_post( $post_id ) {
132
+ return $this->_executor->flush_post( $post_id );
133
+ }
134
+
135
+ /**
136
+ * Checks if page contents can be flushed (i.e. cached at all)
137
+ */
138
+ function flushable_posts( $extras = null ) {
139
+ $flushable_posts = apply_filters( 'w3tc_flushable_posts', false,
140
+ $extras );
141
+ return $flushable_posts;
142
+ }
143
+
144
+ /**
145
+ * Purges/Flushes all posts
146
+ */
147
+ function flush_posts( $extras = null ) {
148
+ return $this->_executor->flush_posts( $extras );
149
+ }
150
+
151
+ /**
152
+ * Purges/Flushes all enabled caches
153
+ */
154
+ function flush_all( $extras = null ) {
155
+ static $flushed = false;
156
+ if ( !$flushed ) {
157
+ $flushed = true;
158
+ $this->_executor->flush_all( $extras );
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Purges/Flushes url
164
+ */
165
+ function flush_url( $url ) {
166
+ static $flushed_urls = array();
167
+
168
+ if ( !in_array( $url, $flushed_urls ) ) {
169
+ $flushed_urls[] = $url;
170
+ return $this->_executor->flush_url( $url );
171
+ }
172
+ return true;
173
+ }
174
+
175
+ /**
176
+ * Makes get request to url specific to post, ie permalinks
177
+ *
178
+ * @param unknown $post_id
179
+ * @return boolean
180
+ */
181
+ function prime_post( $post_id ) {
182
+ return $this->_executor->prime_post( $post_id );
183
+ }
184
+
185
+
186
+
187
+ function execute_delayed_operations() {
188
+ return $this->_executor->execute_delayed_operations();
189
+ }
190
+
191
+
192
+
193
+ function execute_delayed_operations_filter( $v ) {
194
+ $this->execute_delayed_operations();
195
+
196
+ return $v;
197
+ }
198
+ }
CacheFlush_Locally.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Cache flushing
6
+ *
7
+ * priorities are very important for actions here.
8
+ * if e.g. CDN is flushed before local page cache - CDN can cache again
9
+ * still not flushed pages from local page cache.
10
+ * 100 - db
11
+ * 200 - 999 local objects, like object cache
12
+ * 1000 - 1999 local files (minify, pagecache)
13
+ * 2000 - 2999 local reverse proxies varnish, nginx
14
+ * 3000 - external caches like cdn, cloudflare
15
+ */
16
+ class CacheFlush_Locally {
17
+ /**
18
+ * Cleans db cache
19
+ */
20
+ function dbcache_flush( $extras = array() ) {
21
+ if ( isset( $extras['only'] ) && $extras['only'] != 'dbcache' )
22
+ return;
23
+
24
+ do_action( 'w3tc_flush_dbcache' );
25
+
26
+ if ( !method_exists( $GLOBALS['wpdb'], 'flush_cache' ) )
27
+ return false;
28
+
29
+ return $GLOBALS['wpdb']->flush_cache();
30
+ }
31
+
32
+ /**
33
+ * Cleans object cache
34
+ */
35
+ function objectcache_flush( $extras = array() ) {
36
+ if ( isset( $extras['only'] ) && $extras['only'] != 'objectcache' )
37
+ return;
38
+
39
+ do_action( 'w3tc_flush_objectcache' );
40
+ $objectcache = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
41
+ $v = $objectcache->flush();
42
+
43
+ do_action( 'w3tc_flush_after_objectcache' );
44
+
45
+ return $v;
46
+ }
47
+
48
+ /**
49
+ * Cleans fragment cache
50
+ */
51
+ function fragmentcache_flush( $extras = array() ) {
52
+ if ( isset( $extras['only'] ) && $extras['only'] != 'fragment' )
53
+ return;
54
+
55
+ do_action( 'w3tc_flush_fragmentcache' );
56
+ do_action( 'w3tc_flush_after_fragmentcache' );
57
+
58
+ return true;
59
+ }
60
+
61
+ /**
62
+ * Cleans fragment cache
63
+ */
64
+ function fragmentcache_flush_group( $group ) {
65
+ do_action( 'w3tc_flush_fragmentcache_group', $group );
66
+ do_action( 'w3tc_flush_after_fragmentcache_group', $group );
67
+
68
+ return true;
69
+ }
70
+
71
+ function minifycache_flush( $extras = array() ) {
72
+ if ( isset( $extras['only'] ) && $extras['only'] != 'minify' )
73
+ return;
74
+
75
+ do_action( 'w3tc_flush_minify' );
76
+ $minifycache = Dispatcher::component( 'Minify_MinifiedFileRequestHandler' );
77
+ $v = $minifycache->flush();
78
+ do_action( 'w3tc_flush_after_minify' );
79
+
80
+ return $v;
81
+ }
82
+
83
+ function minifycache_flush_all( $extras = array() ) {
84
+ delete_option( 'w3tc_minify' );
85
+ $this->minifycache_flush( $extras );
86
+ }
87
+
88
+ /**
89
+ * Updates Query String
90
+ */
91
+ function browsercache_flush( $extras = array() ) {
92
+ if ( isset( $extras['only'] ) && $extras['only'] != 'browsercache' )
93
+ return;
94
+
95
+ do_action( 'w3tc_flush_browsercache' );
96
+ update_option( 'w3tc_browsercache_flush_timestamp',
97
+ rand( 10000, 99999 ) . '' );
98
+ do_action( 'w3tc_flush_after_browsercache' );
99
+ }
100
+
101
+ /**
102
+ * Purge CDN mirror cache
103
+ */
104
+ function cdn_purge_all() {
105
+ do_action( 'w3tc_cdn_purge_all' );
106
+ $cdn_core = Dispatcher::component( 'Cdn_Core' );
107
+ $cdn = $cdn_core->get_cdn();
108
+ $results = array();
109
+ $v = $cdn->purge_all( $results );
110
+ do_action( 'w3tc_cdn_purge_all_after' );
111
+
112
+ return $v;
113
+ }
114
+
115
+ /**
116
+ * Purges Files from Varnish (If enabled) and CDN
117
+ *
118
+ * @param array $purgefiles array consisting of CdnCommon file descriptors
119
+ * array(array('local_path'=>'', 'remote_path'=> ''))
120
+ * @return boolean
121
+ */
122
+ function cdn_purge_files( $purgefiles ) {
123
+ do_action( 'w3tc_cdn_purge_files', $purgefiles );
124
+ $common = Dispatcher::component( 'Cdn_Core' );
125
+ $results = array();
126
+ $v = $common->purge( $purgefiles, false, $results );
127
+ do_action( 'w3tc_cdn_purge_files_after', $purgefiles );
128
+
129
+ return $v;
130
+ }
131
+
132
+
133
+ /**
134
+ * Flushes the system APC
135
+ *
136
+ * @return bool
137
+ */
138
+ function opcache_flush() {
139
+ $o = Dispatcher::component( 'SystemOpCache_Core' );
140
+ return $o->flush();
141
+ }
142
+
143
+ /**
144
+ * Reload/compile a PHP file
145
+ *
146
+ * @param unknown $filename
147
+ * @return bool
148
+ */
149
+ function opcache_flush_file( $filename ) {
150
+ $o = Dispatcher::component( 'SystemOpCache_Core' );
151
+ return $o->flush_file( $filename );
152
+ }
153
+
154
+ /**
155
+ * Purges/Flushes post from page cache, varnish and cdn cache
156
+ */
157
+ function flush_post( $post_id ) {
158
+ do_action( 'w3tc_flush_post', $post_id );
159
+ }
160
+
161
+ /**
162
+ * Purges/Flushes page contents - page cache, varnish and cdn cache
163
+ * When global changes affect whole content but not internal data structures
164
+ */
165
+ function flush_posts( $extras = null ) {
166
+ do_action( 'w3tc_flush_posts', $extras );
167
+ }
168
+
169
+ /**
170
+ * Flushes all enabled caches.
171
+ */
172
+ function flush_all( $extras ) {
173
+ static $default_actions_added = false;
174
+ if ( !$default_actions_added ) {
175
+ $config = Dispatcher::config();
176
+
177
+ $opcache = Dispatcher::component( 'SystemOpCache_Core' );
178
+ if ( $opcache->is_enabled() )
179
+ add_action( 'w3tc_flush_all',
180
+ array( $this, 'opcache_flush' ),
181
+ 50, 1 );
182
+
183
+ if ( $config->get_boolean( 'dbcache.enabled' ) )
184
+ add_action( 'w3tc_flush_all',
185
+ array( $this, 'dbcache_flush' ),
186
+ 100, 1 );
187
+ if ( $config->get_boolean( 'objectcache.enabled' ) )
188
+ add_action( 'w3tc_flush_all',
189
+ array( $this, 'objectcache_flush' ),
190
+ 200, 1 );
191
+ if ( $config->get_boolean( 'minify.enabled' ) )
192
+ add_action( 'w3tc_flush_all',
193
+ array( $this, 'minifycache_flush_all' ),
194
+ 1000, 1 );
195
+
196
+ $default_actions_added = true;
197
+ }
198
+
199
+ do_action( 'w3tc_flush_all', $extras );
200
+ }
201
+
202
+ /**
203
+ * Purges/Flushes url from page cache, varnish and cdn cache
204
+ */
205
+ function flush_url( $url ) {
206
+ do_action( 'w3tc_flush_url', $url );
207
+ }
208
+
209
+ /**
210
+ * Makes get request to url specific to post, ie permalinks
211
+ *
212
+ * @param unknown $post_id
213
+ * @return mixed
214
+ */
215
+ function prime_post( $post_id ) {
216
+ $pgcache = Dispatcher::component( 'PgCache_Plugin_Admin' );
217
+ return $pgcache->prime_post( $post_id );
218
+ }
219
+
220
+ /**
221
+ * Called at the end of http request processing
222
+ * so that flushers can finish something they've decided to delay
223
+ */
224
+ public function execute_delayed_operations() {
225
+ static $default_actions_added = false;
226
+ if ( !$default_actions_added ) {
227
+ $config = Dispatcher::config();
228
+
229
+ if ( $config->get_boolean( 'pgcache.enabled' ) )
230
+ add_filter( 'w3tc_flush_execute_delayed_operations',
231
+ array( $this, '_execute_delayed_operations_pgcache' ),
232
+ 1100 );
233
+ if ( $config->get_boolean( 'varnish.enabled' ) )
234
+ add_filter( 'w3tc_flush_execute_delayed_operations',
235
+ array( $this, '_execute_delayed_operations_varnish' ),
236
+ 2000 );
237
+ $default_actions_added = true;
238
+ }
239
+
240
+ // build response in a form 'module' => 'error message' (empty if no error)
241
+ $actions_made = array();
242
+ $actions_made = apply_filters( 'w3tc_flush_execute_delayed_operations',
243
+ $actions_made );
244
+
245
+ return $actions_made;
246
+ }
247
+
248
+ public function _execute_delayed_operations_pgcache( $actions_made ) {
249
+ $o = Dispatcher::component( 'PgCache_Flush' );
250
+ $count_flushed = $o->flush_post_cleanup();
251
+ if ( $count_flushed > 0 )
252
+ $actions_made[] = array( 'module' => 'pgcache' );
253
+
254
+ return $actions_made;
255
+ }
256
+
257
+ public function _execute_delayed_operations_varnish( $actions_made ) {
258
+ $o = Dispatcher::component( 'Varnish_Flush' );
259
+ $count_flushed = $o->flush_post_cleanup();
260
+ if ( $count_flushed > 0 )
261
+ $actions_made[] = array( 'module' => 'varnish' );
262
+
263
+ return $actions_made;
264
+ }
265
+ }
Cache_Apc.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * APC class
6
+ */
7
+ class Cache_Apc extends Cache_Base {
8
+
9
+ /*
10
+ * Used for faster flushing
11
+ *
12
+ * @var integer $_key_version
13
+ */
14
+ private $_key_version = array();
15
+
16
+ /**
17
+ * Adds data
18
+ *
19
+ * @param string $key
20
+ * @param mixed $var
21
+ * @param integer $expire
22
+ * @param string $group Used to differentiate between groups of cache values
23
+ * @return boolean
24
+ */
25
+ function add( $key, &$var, $expire = 0, $group = '' ) {
26
+ if ( $this->get( $key, $group ) === false ) {
27
+ return $this->set( $key, $var, $expire, $group );
28
+ }
29
+
30
+ return false;
31
+ }
32
+
33
+ /**
34
+ * Sets data
35
+ *
36
+ * @param string $key
37
+ * @param mixed $var
38
+ * @param integer $expire
39
+ * @param string $group Used to differentiate between groups of cache values
40
+ * @return boolean
41
+ */
42
+ function set( $key, $var, $expire = 0, $group = '' ) {
43
+ $var['key_version'] = $this->_get_key_version( $group );
44
+
45
+ $storage_key = $this->get_item_key( $key );
46
+ return apc_store( $storage_key, serialize( $var ), $expire );
47
+ }
48
+
49
+ /**
50
+ * Returns data
51
+ *
52
+ * @param string $key
53
+ * @param string $group Used to differentiate between groups of cache values
54
+ * @return mixed
55
+ */
56
+ function get_with_old( $key, $group = '' ) {
57
+ $has_old_data = false;
58
+ $storage_key = $this->get_item_key( $key );
59
+
60
+ $v = @unserialize( apc_fetch( $storage_key ) );
61
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
62
+ return array( null, $has_old_data );
63
+
64
+ $key_version = $this->_get_key_version( $group );
65
+ if ( $v['key_version'] == $key_version )
66
+ return array( $v, $has_old_data );
67
+
68
+ if ( $v['key_version'] > $key_version ) {
69
+ $this->_set_key_version( $v['key_version'], $group );
70
+ return array( $v, $has_old_data );
71
+ }
72
+
73
+ // key version is old
74
+ if ( !$this->_use_expired_data )
75
+ return array( null, $has_old_data );
76
+
77
+ // if we have expired data - update it for future use and let
78
+ // current process recalculate it
79
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
80
+ if ( $expires_at == null || time() > $expires_at ) {
81
+ $v['expires_at'] = time() + 30;
82
+ apc_store( $storage_key, serialize( $v ), 0 );
83
+ $has_old_data = true;
84
+
85
+ return array( null, $has_old_data );
86
+ }
87
+
88
+ // return old version
89
+ return array( $v, $has_old_data );
90
+ }
91
+
92
+
93
+ /**
94
+ * Replaces data
95
+ *
96
+ * @param string $key
97
+ * @param mixed $var
98
+ * @param integer $expire
99
+ * @param string $group Used to differentiate between groups of cache values
100
+ * @return boolean
101
+ */
102
+ function replace( $key, &$var, $expire = 0, $group ='' ) {
103
+ if ( $this->get( $key, $group ) !== false ) {
104
+ return $this->set( $key, $var, $expire, $group );
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Deletes data
112
+ *
113
+ * @param string $key
114
+ * @param string $group
115
+ * @return boolean
116
+ */
117
+ function delete( $key, $group = '' ) {
118
+ $storage_key = $this->get_item_key( $key );
119
+
120
+ if ( $this->_use_expired_data ) {
121
+ $v = @unserialize( apc_fetch( $storage_key ) );
122
+ if ( is_array( $v ) ) {
123
+ $v['key_version'] = 0;
124
+ apc_store( $storage_key, serialize( $v ), 0 );
125
+ return true;
126
+ }
127
+ }
128
+
129
+ return apc_delete( $storage_key );
130
+ }
131
+
132
+ /**
133
+ * Key to delete, deletes .old and primary if exists.
134
+ *
135
+ * @param unknown $key
136
+ * @return bool
137
+ */
138
+ function hard_delete( $key ) {
139
+ $storage_key = $this->get_item_key( $key );
140
+ return apc_delete( $storage_key );
141
+ }
142
+
143
+ /**
144
+ * Flushes all data
145
+ *
146
+ * @param string $group Used to differentiate between groups of cache values
147
+ * @return boolean
148
+ */
149
+ function flush( $group = '' ) {
150
+ $this->_get_key_version( $group ); // initialize $this->_key_version
151
+ $this->_key_version[$group]++;
152
+ $this->_set_key_version( $this->_key_version[$group], $group );
153
+ return true;
154
+ }
155
+
156
+ /**
157
+ * Checks if engine can function properly in this environment
158
+ *
159
+ * @return bool
160
+ */
161
+ public function available() {
162
+ return function_exists( 'apc_store' );
163
+ }
164
+
165
+ /**
166
+ * Returns key postfix
167
+ *
168
+ * @param string $group Used to differentiate between groups of cache values
169
+ * @return integer
170
+ */
171
+ private function _get_key_version( $group = '' ) {
172
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
173
+ $v = apc_fetch( $this->_get_key_version_key( $group ) );
174
+ $v = intval( $v );
175
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
176
+ }
177
+
178
+ return $this->_key_version[$group];
179
+ }
180
+
181
+ /**
182
+ * Sets new key version
183
+ *
184
+ * @param unknown $v
185
+ * @param string $group Used to differentiate between groups of cache values
186
+ * @return boolean
187
+ */
188
+ private function _set_key_version( $v, $group = '' ) {
189
+ apc_store( $this->_get_key_version_key( $group ), $v, 0 );
190
+ }
191
+
192
+ /**
193
+ * Used to replace as atomically as possible known value to new one
194
+ */
195
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
196
+ // apc_cas doesnt fit here, since we are float but it works with
197
+ // int only
198
+ // cant guarantee atomic action here, filelocks fail often
199
+ $value = $this->get( $key );
200
+ if ( isset( $old_value['content'] ) &&
201
+ $value['content'] != $old_value['content'] )
202
+ return false;
203
+
204
+ return $this->set( $key, $new_value );
205
+ }
206
+
207
+ /**
208
+ * Use key as a counter and add integet value to it
209
+ */
210
+ public function counter_add( $key, $value ) {
211
+ if ( $value == 0 )
212
+ return true;
213
+
214
+ $storage_key = $this->get_item_key( $key );
215
+ $r = apc_inc( $storage_key, $value );
216
+ if ( !$r ) // it doesnt initialize counter by itself
217
+ $this->counter_set( $key, 0 );
218
+
219
+ return $r;
220
+ }
221
+
222
+ /**
223
+ * Use key as a counter and add integet value to it
224
+ */
225
+ public function counter_set( $key, $value ) {
226
+ $storage_key = $this->get_item_key( $key );
227
+ return apc_store( $storage_key, $value );
228
+ }
229
+
230
+ /**
231
+ * Get counter's value
232
+ */
233
+ public function counter_get( $key ) {
234
+ $storage_key = $this->get_item_key( $key );
235
+ $v = (int)apc_fetch( $storage_key );
236
+
237
+ return $v;
238
+ }
239
+ }
Cache_Apcu.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * APC class
6
+ */
7
+ class Cache_Apcu extends Cache_Base {
8
+ /*
9
+ * Used for faster flushing
10
+ *
11
+ * @var integer $_key_version
12
+ */
13
+ private $_key_version = array();
14
+
15
+ /**
16
+ * Adds data
17
+ *
18
+ * @param string $key
19
+ * @param mixed $var
20
+ * @param integer $expire
21
+ * @param string $group Used to differentiate between groups of cache values
22
+ * @return boolean
23
+ */
24
+ function add( $key, &$var, $expire = 0, $group = '' ) {
25
+ if ( $this->get( $key, $group ) === false ) {
26
+ return $this->set( $key, $var, $expire, $group );
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ /**
33
+ * Sets data
34
+ *
35
+ * @param string $key
36
+ * @param mixed $var
37
+ * @param integer $expire
38
+ * @param string $group Used to differentiate between groups of cache values
39
+ * @return boolean
40
+ */
41
+ function set( $key, $var, $expire = 0, $group = '' ) {
42
+ $var['key_version'] = $this->_get_key_version( $group );
43
+
44
+ $storage_key = $this->get_item_key( $key );
45
+ return apcu_store( $storage_key, serialize( $var ), $expire );
46
+ }
47
+
48
+ /**
49
+ * Returns data
50
+ *
51
+ * @param string $key
52
+ * @param string $group Used to differentiate between groups of cache values
53
+ * @return mixed
54
+ */
55
+ function get_with_old( $key, $group = '' ) {
56
+ $has_old_data = false;
57
+ $storage_key = $this->get_item_key( $key );
58
+
59
+ $v = @unserialize( apcu_fetch( $storage_key ) );
60
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
61
+ return array( null, $has_old_data );
62
+
63
+ $key_version = $this->_get_key_version( $group );
64
+ if ( $v['key_version'] == $key_version )
65
+ return array( $v, $has_old_data );
66
+
67
+ if ( $v['key_version'] > $key_version ) {
68
+ $this->_set_key_version( $v['key_version'], $group );
69
+ return array( $v, $has_old_data );
70
+ }
71
+
72
+ // key version is old
73
+ if ( !$this->_use_expired_data )
74
+ return array( null, $has_old_data );
75
+
76
+ // if we have expired data - update it for future use and let
77
+ // current process recalculate it
78
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
79
+ if ( $expires_at == null || time() > $expires_at ) {
80
+ $v['expires_at'] = time() + 30;
81
+ apcu_store( $storage_key, serialize( $v ), 0 );
82
+ $has_old_data = true;
83
+
84
+ return array( null, $has_old_data );
85
+ }
86
+
87
+ // return old version
88
+ return array( $v, $has_old_data );
89
+ }
90
+
91
+
92
+ /**
93
+ * Replaces data
94
+ *
95
+ * @param string $key
96
+ * @param mixed $var
97
+ * @param integer $expire
98
+ * @param string $group Used to differentiate between groups of cache values
99
+ * @return boolean
100
+ */
101
+ function replace( $key, &$var, $expire = 0, $group ='' ) {
102
+ if ( $this->get( $key, $group ) !== false ) {
103
+ return $this->set( $key, $var, $expire, $group );
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ /**
110
+ * Deletes data
111
+ *
112
+ * @param string $key
113
+ * @param string $group
114
+ * @return boolean
115
+ */
116
+ function delete( $key, $group = '' ) {
117
+ $storage_key = $this->get_item_key( $key );
118
+
119
+ if ( $this->_use_expired_data ) {
120
+ $v = @unserialize( apcu_fetch( $storage_key ) );
121
+ if ( is_array( $v ) ) {
122
+ $v['key_version'] = 0;
123
+ apcu_store( $storage_key, serialize( $v ), 0 );
124
+ return true;
125
+ }
126
+ }
127
+
128
+ return apcu_delete( $storage_key );
129
+ }
130
+
131
+ /**
132
+ * Key to delete, deletes .old and primary if exists.
133
+ *
134
+ * @param unknown $key
135
+ * @return bool
136
+ */
137
+ function hard_delete( $key ) {
138
+ $storage_key = $this->get_item_key( $key );
139
+ return apcu_delete( $storage_key );
140
+ }
141
+
142
+ /**
143
+ * Flushes all data
144
+ *
145
+ * @param string $group Used to differentiate between groups of cache values
146
+ * @return boolean
147
+ */
148
+ function flush( $group = '' ) {
149
+ $this->_get_key_version( $group ); // initialize $this->_key_version
150
+ $this->_key_version[$group]++;
151
+ $this->_set_key_version( $this->_key_version[$group], $group );
152
+ return true;
153
+ }
154
+
155
+ /**
156
+ * Checks if engine can function properly in this environment
157
+ *
158
+ * @return bool
159
+ */
160
+ public function available() {
161
+ return function_exists( 'apcu_store' );
162
+ }
163
+
164
+ /**
165
+ * Returns key postfix
166
+ *
167
+ * @param string $group Used to differentiate between groups of cache values
168
+ * @return integer
169
+ */
170
+ private function _get_key_version( $group = '' ) {
171
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
172
+ $v = apcu_fetch( $this->_get_key_version_key( $group ) );
173
+ $v = intval( $v );
174
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
175
+ }
176
+
177
+ return $this->_key_version[$group];
178
+ }
179
+
180
+ /**
181
+ * Sets new key version
182
+ *
183
+ * @param unknown $v
184
+ * @param string $group Used to differentiate between groups of cache values
185
+ * @return boolean
186
+ */
187
+ private function _set_key_version( $v, $group = '' ) {
188
+ apcu_store( $this->_get_key_version_key( $group ), $v, 0 );
189
+ }
190
+
191
+ /**
192
+ * Used to replace as atomically as possible known value to new one
193
+ */
194
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
195
+ // apc_cas doesnt fit here, since we are float but it works with
196
+ // int only
197
+ // cant guarantee atomic action here, filelocks fail often
198
+ $value = $this->get( $key );
199
+ if ( isset( $old_value['content'] ) &&
200
+ $value['content'] != $old_value['content'] )
201
+ return false;
202
+
203
+ return $this->set( $key, $new_value );
204
+ }
205
+
206
+ /**
207
+ * Use key as a counter and add integet value to it
208
+ */
209
+ public function counter_add( $key, $value ) {
210
+ if ( $value == 0 )
211
+ return true;
212
+
213
+ $storage_key = $this->get_item_key( $key );
214
+ $r = apcu_inc( $storage_key, $value );
215
+ if ( !$r ) // it doesnt initialize counter by itself
216
+ $this->counter_set( $key, 0 );
217
+
218
+ return $r;
219
+ }
220
+
221
+ /**
222
+ * Use key as a counter and add integet value to it
223
+ */
224
+ public function counter_set( $key, $value ) {
225
+ $storage_key = $this->get_item_key( $key );
226
+ return apcu_store( $storage_key, $value );
227
+ }
228
+
229
+ /**
230
+ * Get counter's value
231
+ */
232
+ public function counter_get( $key ) {
233
+ $storage_key = $this->get_item_key( $key );
234
+ $v = (int)apcu_fetch( $storage_key );
235
+
236
+ return $v;
237
+ }
238
+ }
Cache_Base.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Base cache class
6
+ */
7
+
8
+ /**
9
+ * class Cache_Base
10
+ */
11
+ class Cache_Base {
12
+ /*
13
+ * Blog id
14
+ *
15
+ * @var integer
16
+ */
17
+ protected $_blog_id = 0;
18
+
19
+ /**
20
+ * To separate the caching for different modules
21
+ *
22
+ * @var string
23
+ */
24
+ protected $_module = '';
25
+
26
+ /**
27
+ * Host
28
+ *
29
+ * @var string
30
+ */
31
+ protected $_host = '';
32
+
33
+ /**
34
+ * Host
35
+ *
36
+ * @var int
37
+ */
38
+ protected $_instance_id = 0;
39
+
40
+ /*
41
+ * If we are going to return expired data when some other process
42
+ * is working on new data calculation
43
+ *
44
+ * @var boolean
45
+ */
46
+ protected $_use_expired_data = false;
47
+
48
+ /**
49
+ * Constructor
50
+ *
51
+ * @param array $config
52
+ */
53
+ public function __construct( $config = array() ) {
54
+ $this->_blog_id = $config['blog_id'];
55
+ $this->_use_expired_data = isset( $config['use_expired_data'] )?$config['use_expired_data']:false;
56
+ $this->_module = isset( $config['module'] ) ? $config['module'] : 'default';
57
+ $this->_host = isset( $config['host'] ) ? $config['host'] : '';
58
+ $this->_instance_id = isset( $config['instance_id'] ) ? $config['instance_id'] : 0;
59
+ }
60
+ /**
61
+ * Adds data
62
+ *
63
+ * @abstract
64
+ * @param string $key
65
+ * @param mixed $data
66
+ * @param integer $expire
67
+ * @param string $group Used to differentiate between groups of cache values
68
+ * @return boolean
69
+ */
70
+ function add( $key, &$data, $expire = 0, $group = '' ) {
71
+ return false;
72
+ }
73
+
74
+ /**
75
+ * Sets data
76
+ *
77
+ * @abstract
78
+ * @param string $key
79
+ * @param mixed $data
80
+ * @param integer $expire
81
+ * @param string $group Used to differentiate between groups of cache values
82
+ * @return boolean
83
+ */
84
+ function set( $key, $data, $expire = 0, $group = '' ) {
85
+ return false;
86
+ }
87
+
88
+ /**
89
+ * Returns data
90
+ *
91
+ * @param string $key
92
+ * @param string $group Used to differentiate between groups of cache values
93
+ * @return mixed
94
+ */
95
+ function get( $key, $group = '' ) {
96
+ list( $data, $has_old ) = $this->get_with_old( $key, $group );
97
+ return $data;
98
+ }
99
+
100
+ /**
101
+ * Return primary data and if old exists
102
+ *
103
+ * @abstract
104
+ * @param string $key
105
+ * @param string $group Used to differentiate between groups of cache values
106
+ * @return array|mixed
107
+ */
108
+ function get_with_old( $key, $group = '' ) {
109
+ return array( null, false );
110
+ }
111
+
112
+ /**
113
+ * Alias for get for minify cache
114
+ *
115
+ * @param string $key
116
+ * @param string $group Used to differentiate between groups of cache values
117
+ * @return mixed
118
+ */
119
+ function fetch( $key, $group = '' ) {
120
+ return $this->get( $key, $group = '' );
121
+ }
122
+
123
+ /**
124
+ * Replaces data
125
+ *
126
+ * @abstract
127
+ * @param string $key
128
+ * @param mixed $data
129
+ * @param integer $expire
130
+ * @param string $group Used to differentiate between groups of cache values
131
+ * @return boolean
132
+ */
133
+ function replace( $key, &$data, $expire = 0, $group = '' ) {
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Deletes data
139
+ *
140
+ * @abstract
141
+ * @param string $key
142
+ * @param string $group Used to differentiate between groups of cache values
143
+ * @return boolean
144
+ */
145
+ function delete( $key, $group = '' ) {
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Deletes primary data and old data
151
+ *
152
+ * @abstract
153
+ * @param string $key
154
+ * @return boolean
155
+ */
156
+ function hard_delete( $key ) {
157
+ return false;
158
+ }
159
+
160
+ /**
161
+ * Flushes all data
162
+ *
163
+ * @abstract
164
+ * @param string $group Used to differentiate between groups of cache values
165
+ * @return boolean
166
+ */
167
+ function flush( $group = '' ) {
168
+ return false;
169
+ }
170
+
171
+ /**
172
+ * Checks if engine can function properly in this environment
173
+ *
174
+ * @return bool
175
+ */
176
+ public function available() {
177
+ return true;
178
+ }
179
+
180
+ /**
181
+ * Constructs key version key
182
+ *
183
+ * @param unknown $group
184
+ * @return string
185
+ */
186
+ protected function _get_key_version_key( $group = '' ) {
187
+ return sprintf( 'w3tc_%d_%s_%s_%d_key_version', $this->_blog_id, $this->_module, $group, $this->_instance_id );
188
+ }
189
+
190
+ /**
191
+ * Constructs item key
192
+ *
193
+ * @param unknown $name
194
+ * @return string
195
+ */
196
+ public function get_item_key( $name ) {
197
+ $key = sprintf( 'w3tc_%s_%d_%s_%s', $this->_host, $this->_blog_id, $this->_module, $name );
198
+ return $key;
199
+ }
200
+
201
+
202
+ /**
203
+ * Use key as a counter and add integet value to it
204
+ */
205
+ public function counter_add( $key, $value ) {
206
+ return false;
207
+ }
208
+
209
+ /**
210
+ * Use key as a counter and add integet value to it
211
+ */
212
+ public function counter_set( $key, $value ) {
213
+ return false;
214
+ }
215
+
216
+ /**
217
+ * Get counter's value
218
+ */
219
+ public function counter_get( $key ) {
220
+ return false;
221
+ }
222
+ }
Cache_Eaccelerator.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * eAccelerator class
6
+ */
7
+ class Cache_Eaccelerator extends Cache_Base {
8
+
9
+ /*
10
+ * Used for faster flushing
11
+ *
12
+ * @var integer $_key_postfix
13
+ */
14
+ private $_key_version = array();
15
+
16
+ /**
17
+ * Adds data
18
+ *
19
+ * @param string $key
20
+ * @param mixed $var
21
+ * @param integer $expire
22
+ * @param string $group Used to differentiate between groups of cache values
23
+ * @return boolean
24
+ */
25
+ function add( $key, &$var, $expire = 0, $group = '' ) {
26
+ if ( $this->get( $key, $group ) === false ) {
27
+ return $this->set( $key, $var, $expire, $group );
28
+ }
29
+
30
+ return false;
31
+ }
32
+
33
+ /**
34
+ * Sets data
35
+ *
36
+ * @param string $key
37
+ * @param mixed $var
38
+ * @param integer $expire
39
+ * @param string $group Used to differentiate between groups of cache values
40
+ * @return boolean
41
+ */
42
+ function set( $key, $var, $expire = 0, $group = '' ) {
43
+ $var['key_version'] = $this->_get_key_version( $group );
44
+
45
+ $storage_key = $this->get_item_key( $key );
46
+ return eaccelerator_put( $storage_key, serialize( $var ), $expire );
47
+ }
48
+
49
+ /**
50
+ * Returns data
51
+ *
52
+ * @param string $key
53
+ * @param string $group Used to differentiate between groups of cache values
54
+ * @return mixed
55
+ */
56
+ function get_with_old( $key, $group = '' ) {
57
+ $has_old_data = false;
58
+ $storage_key = $this->get_item_key( $key );
59
+
60
+ $v = @unserialize( eaccelerator_get( $storage_key ) );
61
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
62
+ return array( null, $has_old_data );
63
+
64
+ $key_version = $this->_get_key_version( $group );
65
+ if ( $v['key_version'] == $key_version )
66
+ return array( $v, $has_old_data );
67
+
68
+ if ( $v['key_version'] > $key_version ) {
69
+ $this->_set_key_version( $v['key_version'], $group );
70
+ return array( $v, $has_old_data );
71
+ }
72
+
73
+ // key version is old
74
+ if ( !$this->_use_expired_data )
75
+ return array( null, $has_old_data );
76
+
77
+ // if we have expired data - update it for future use and let
78
+ // current process recalculate it
79
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
80
+ if ( $expires_at == null || time() > $expires_at ) {
81
+ $v['expires_at'] = time() + 30;
82
+ eaccelerator_put( $storage_key, serialize( $v ), 0 );
83
+ $has_old_data = true;
84
+
85
+ return array( null, $has_old_data );
86
+ }
87
+
88
+ // return old version
89
+ return array( $v, $has_old_data );
90
+ }
91
+
92
+ /**
93
+ * Replaces data
94
+ *
95
+ * @param string $key
96
+ * @param mixed $var
97
+ * @param integer $expire
98
+ * @param string $group Used to differentiate between groups of cache values
99
+ * @return boolean
100
+ */
101
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
102
+ if ( $this->get( $key, $group ) !== false ) {
103
+ return $this->set( $key, $var, $expire, $group );
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ /**
110
+ * Deletes data
111
+ *
112
+ * @param string $key
113
+ * @param string $group
114
+ * @return boolean
115
+ */
116
+ function delete( $key, $group = '' ) {
117
+ $storage_key = $this->get_item_key( $key );
118
+
119
+ if ( $this->_use_expired_data ) {
120
+ $v = @unserialize( eaccelerator_get( $storage_key ) );
121
+ if ( is_array( $v ) ) {
122
+ $v['key_version'] = 0;
123
+ eaccelerator_put( $storage_key, serialize( $v ), 0 );
124
+ return true;
125
+ }
126
+ }
127
+
128
+ return eaccelerator_rm( $key . '_' . $this->_blog_id );
129
+ }
130
+
131
+
132
+ /**
133
+ * Key to delete, deletes .old and primary if exists.
134
+ *
135
+ * @param unknown $key
136
+ * @return bool
137
+ */
138
+ function hard_delete( $key ) {
139
+ $storage_key = $this->get_item_key( $key );
140
+ return eaccelerator_rm( $storage_key );
141
+ }
142
+ /**
143
+ * Flushes all data
144
+ *
145
+ * @param string $group Used to differentiate between groups of cache values
146
+ * @return boolean
147
+ */
148
+ function flush( $group = '' ) {
149
+ $this->_get_key_version( $group ); // initialize $this->_key_version
150
+ $this->_key_version[$group]++;
151
+ $this->_set_key_version( $this->_key_version[$group], $group );
152
+
153
+ return true;
154
+ }
155
+
156
+ /**
157
+ * Checks if engine can function properly in this environment
158
+ *
159
+ * @return bool
160
+ */
161
+ public function available() {
162
+ return function_exists( 'eaccelerator_put' );
163
+ }
164
+
165
+ /**
166
+ * Returns key postfix
167
+ *
168
+ * @param string $group Used to differentiate between groups of cache values
169
+ * @return integer
170
+ */
171
+ private function _get_key_version( $group = '' ) {
172
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
173
+ $v = eaccelerator_get( $this->_get_key_version_key( $group ) );
174
+ $v = intval( $v );
175
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
176
+ }
177
+
178
+ return $this->_key_version[$group];
179
+ }
180
+
181
+ /**
182
+ * Sets new key version
183
+ *
184
+ * @param unknown $v
185
+ * @param string $group Used to differentiate between groups of cache values
186
+ * @return boolean
187
+ */
188
+ private function _set_key_version( $v, $group = '' ) {
189
+ // cant guarantee atomic action here, filelocks fail often
190
+ $value = $this->get( $key );
191
+ if ( isset( $old_value['content'] ) &&
192
+ $value['content'] != $old_value['content'] )
193
+ return false;
194
+
195
+ return $this->set( $key, $new_value );
196
+ }
197
+
198
+ /**
199
+ * Used to replace as atomically as possible known value to new one
200
+ */
201
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
202
+ // eaccelerator cache not supported anymore by its authors
203
+ return false;
204
+ }
205
+
206
+ /**
207
+ * Use key as a counter and add integet value to it
208
+ */
209
+ public function counter_add( $key, $value ) {
210
+ // eaccelerator cache not supported anymore by its authors
211
+ return false;
212
+ }
213
+
214
+ /**
215
+ * Use key as a counter and add integet value to it
216
+ */
217
+ public function counter_set( $key, $value ) {
218
+ // eaccelerator cache not supported anymore by its authors
219
+ return false;
220
+ }
221
+
222
+ /**
223
+ * Get counter's value
224
+ */
225
+ public function counter_get( $key ) {
226
+ // eaccelerator cache not supported anymore by its authors
227
+ return false;
228
+ }
229
+ }
Cache_File.php ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class Cache_File
6
+ */
7
+ class Cache_File extends Cache_Base {
8
+ /**
9
+ * Path to cache dir
10
+ *
11
+ * @var string
12
+ */
13
+ protected $_cache_dir = '';
14
+
15
+ /**
16
+ * Directory to flush
17
+ *
18
+ * @var string
19
+ */
20
+ protected $_flush_dir = '';
21
+ /**
22
+ * Exclude files
23
+ *
24
+ * @var array
25
+ */
26
+ protected $_exclude = array();
27
+
28
+ /**
29
+ * Flush time limit
30
+ *
31
+ * @var int
32
+ */
33
+ protected $_flush_timelimit = 0;
34
+
35
+ /**
36
+ * File locking
37
+ *
38
+ * @var boolean
39
+ */
40
+ protected $_locking = false;
41
+
42
+ /**
43
+ * If path should be generated based on wp_hash
44
+ *
45
+ * @var bool
46
+ */
47
+ protected $_use_wp_hash = false;
48
+
49
+ /**
50
+ * Constructor
51
+ *
52
+ * @param array $config
53
+ */
54
+ function __construct( $config = array() ) {
55
+ parent::__construct( $config );
56
+ if ( isset( $config['cache_dir'] ) )
57
+ $this->_cache_dir = trim( $config['cache_dir'] );
58
+ else
59
+ $this->_cache_dir = Util_Environment::cache_blog_dir( $config['section'], $config['blog_id'] );
60
+
61
+ $this->_exclude = isset( $config['exclude'] ) ? (array) $config['exclude'] : array();
62
+ $this->_flush_timelimit = isset( $config['flush_timelimit'] ) ? (int) $config['flush_timelimit'] : 180;
63
+ $this->_locking = isset( $config['locking'] ) ? (boolean) $config['locking'] : false;
64
+
65
+ if ( isset( $config['flush_dir'] ) )
66
+ $this->_flush_dir = $config['flush_dir'];
67
+ else {
68
+ if ( $config['blog_id'] <= 0 && !isset( $config['cache_dir'] ) ) {
69
+ // clear whole section if we operate on master cache
70
+ // and in a mode when cache_dir not strictly specified
71
+ $this->_flush_dir = Util_Environment::cache_dir( $config['section'] );
72
+ } else
73
+ $this->_flush_dir = $this->_cache_dir;
74
+ }
75
+ if ( isset( $config['use_wp_hash'] ) && $config['use_wp_hash'] )
76
+ $this->_use_wp_hash = true;
77
+ }
78
+
79
+ /**
80
+ * Adds data
81
+ *
82
+ * @param string $key
83
+ * @param mixed $var
84
+ * @param integer $expire
85
+ * @param string $group Used to differentiate between groups of cache values
86
+ * @return boolean
87
+ */
88
+ function add( $key, &$var, $expire = 0, $group = '' ) {
89
+ if ( $this->get( $key, $group ) === false ) {
90
+ return $this->set( $key, $var, $expire, $group );
91
+ }
92
+
93
+ return false;
94
+ }
95
+
96
+ /**
97
+ * Sets data
98
+ *
99
+ * @param string $key
100
+ * @param mixed $var
101
+ * @param integer $expire
102
+ * @param string $group Used to differentiate between groups of cache values
103
+ * @return boolean
104
+ */
105
+ function set( $key, $var, $expire = 0, $group = '' ) {
106
+ $fp = $this->fopen_write( $key, $group, 'wb' );
107
+ if ( !$fp )
108
+ return false;
109
+
110
+ if ( $this->_locking )
111
+ @flock( $fp, LOCK_EX );
112
+
113
+ if ( $expire <= 0 || $expire > W3TC_CACHE_FILE_EXPIRE_MAX )
114
+ $expire = W3TC_CACHE_FILE_EXPIRE_MAX;
115
+
116
+ $expires_at = time() + $expire;
117
+ @fputs( $fp, pack( 'L', $expires_at ) );
118
+ @fputs( $fp, '<?php exit; ?>' );
119
+ @fputs( $fp, @serialize( $var ) );
120
+ @fclose( $fp );
121
+
122
+ if ( $this->_locking )
123
+ @flock( $fp, LOCK_UN );
124
+
125
+ return true;
126
+ }
127
+
128
+ /**
129
+ * Returns data
130
+ *
131
+ * @param string $key
132
+ * @param string $group Used to differentiate between groups of cache values
133
+ * @return mixed
134
+ */
135
+ function get_with_old( $key, $group = '' ) {
136
+ list( $data, $has_old_data ) = $this->_get_with_old_raw( $key, $group );
137
+ if ( !empty( $data ) )
138
+ $data_unserialized = @unserialize( $data );
139
+ else
140
+ $data_unserialized = $data;
141
+
142
+ return array( $data_unserialized, $has_old_data );
143
+ }
144
+
145
+
146
+
147
+ private function _get_with_old_raw( $key, $group = '' ) {
148
+ $has_old_data = false;
149
+
150
+ $storage_key = $this->get_item_key( $key );
151
+
152
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
153
+ ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
154
+ $this->_get_path( $storage_key );
155
+ if ( !is_readable( $path ) )
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 );
164
+
165
+ $expires_at = @fread( $fp, 4 );
166
+ $data = null;
167
+
168
+ if ( $expires_at !== false ) {
169
+ list( , $expires_at ) = @unpack( 'L', $expires_at );
170
+
171
+ if ( time() > $expires_at ) {
172
+ if ( $this->_use_expired_data ) {
173
+ // update expiration so other threads will use old data
174
+ $fp2 = @fopen( $path, 'cb' );
175
+
176
+ if ( $fp2 ) {
177
+ @fputs( $fp2, pack( 'L', time() + 30 ) );
178
+ @fclose( $fp2 );
179
+ }
180
+ $has_old_data = true;
181
+ }
182
+ } else {
183
+ $data = '';
184
+
185
+ while ( !@feof( $fp ) ) {
186
+ $data .= @fread( $fp, 4096 );
187
+ }
188
+ $data = substr( $data, 14 );
189
+ }
190
+
191
+ }
192
+
193
+ if ( $this->_locking )
194
+ @flock( $fp, LOCK_UN );
195
+
196
+ @fclose( $fp );
197
+
198
+ return array( $data, $has_old_data );
199
+ }
200
+
201
+ /**
202
+ * Replaces data
203
+ *
204
+ * @param string $key
205
+ * @param mixed $var
206
+ * @param integer $expire
207
+ * @param string $group Used to differentiate between groups of cache values
208
+ * @return boolean
209
+ */
210
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
211
+ if ( $this->get( $key, $group ) !== false ) {
212
+ return $this->set( $key, $var, $expire, $group );
213
+ }
214
+
215
+ return false;
216
+ }
217
+
218
+ /**
219
+ * Deletes data
220
+ *
221
+ * @param string $key
222
+ * @param string $group Used to differentiate between groups of cache values
223
+ * @return boolean
224
+ */
225
+ function delete( $key, $group = '' ) {
226
+ $storage_key = $this->get_item_key( $key );
227
+
228
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
229
+ ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
230
+ $this->_get_path( $storage_key );
231
+
232
+ if ( !file_exists( $path ) )
233
+ return true;
234
+
235
+ if ( $this->_use_expired_data ) {
236
+ $fp = @fopen( $path, 'cb' );
237
+
238
+ if ( $fp ) {
239
+ if ( $this->_locking )
240
+ @flock( $fp, LOCK_EX );
241
+
242
+ @fputs( $fp, pack( 'L', 0 ) ); // make it expired
243
+ @fclose( $fp );
244
+
245
+ if ( $this->_locking )
246
+ @flock( $fp, LOCK_UN );
247
+ return true;
248
+ }
249
+
250
+ }
251
+
252
+ return @unlink( $path );
253
+ }
254
+
255
+ /**
256
+ * Key to delete, deletes .old and primary if exists.
257
+ *
258
+ * @param string $key
259
+ *
260
+ * @return bool
261
+ */
262
+ function hard_delete( $key ) {
263
+ $key = $this->get_item_key( $key );
264
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR . $this->_get_path( $key );
265
+ return @unlink( $path );
266
+ }
267
+
268
+ /**
269
+ * Flushes all data
270
+ *
271
+ * @param string $group Used to differentiate between groups of cache values
272
+ * @return boolean
273
+ */
274
+ function flush( $group = '' ) {
275
+ @set_time_limit( $this->_flush_timelimit );
276
+ $flush_dir = $group ?
277
+ $this->_cache_dir . DIRECTORY_SEPARATOR . $group .
278
+ DIRECTORY_SEPARATOR :
279
+ $this->_flush_dir;
280
+ Util_File::emptydir( $flush_dir, $this->_exclude );
281
+ return true;
282
+ }
283
+
284
+ /**
285
+ * Returns modification time of cache file
286
+ *
287
+ * @param integer $key
288
+ * @param string $group Used to differentiate between groups of cache values
289
+ * @return boolean|string
290
+ */
291
+ function mtime( $key, $group = '' ) {
292
+ $path =
293
+ $this->_cache_dir . DIRECTORY_SEPARATOR .
294
+ ( $group ? $group . DIRECTORY_SEPARATOR : '' ) .
295
+ $this->_get_path( $key );
296
+
297
+ if ( file_exists( $path ) ) {
298
+ return @filemtime( $path );
299
+ }
300
+
301
+ return false;
302
+ }
303
+
304
+ /**
305
+ * Returns file path for key
306
+ *
307
+ * @param string $key
308
+ * @return string
309
+ */
310
+ function _get_path( $key ) {
311
+ if ( $this->_use_wp_hash && function_exists( 'wp_hash' ) )
312
+ $hash = wp_hash( $key );
313
+ else
314
+ $hash = md5( $key );
315
+
316
+ $path = sprintf( '%s/%s/%s.php', substr( $hash, 0, 3 ), substr( $hash, 3, 3 ), $hash );
317
+
318
+ return $path;
319
+ }
320
+
321
+ public function get_stats_size( $timeout_time ) {
322
+ $size = array(
323
+ 'bytes' => 0,
324
+ 'items' => 0,
325
+ 'timeout_occurred' => false
326
+ );
327
+
328
+ $size = $this->dirsize( $this->_cache_dir, $size, $timeout_time );
329
+ return $size;
330
+ }
331
+
332
+
333
+
334
+ private function dirsize( $path, $size, $timeout_time ) {
335
+ $dir = @opendir( $path );
336
+
337
+ if ( $dir ) {
338
+ while ( !$size['timeout_occurred'] && ( $entry = @readdir( $dir ) ) !== false ) {
339
+ if ( $entry == '.' || $entry == '..' ) {
340
+ continue;
341
+ }
342
+
343
+ $full_path = $path . DIRECTORY_SEPARATOR . $entry;
344
+
345
+ if ( @is_dir( $full_path ) ) {
346
+ $size = $this->dirsize( $full_path, $size, $timeout_time );
347
+ } else {
348
+ $size['bytes'] += @filesize( $full_path );
349
+
350
+ // dont check time() for each file, quite expensive
351
+ $size['items']++;
352
+ if ( $size['items'] % 1000 == 0 )
353
+ $size['timeout_occurred'] |= ( time() > $timeout_time );
354
+ }
355
+ }
356
+
357
+ @closedir( $dir );
358
+ }
359
+
360
+ return $size;
361
+ }
362
+
363
+ /**
364
+ * Used to replace as atomically as possible known value to new one
365
+ */
366
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
367
+ // cant guarantee atomic action here, filelocks fail often
368
+ $value = $this->get( $key );
369
+ if ( isset( $old_value['content'] ) &&
370
+ $value['content'] != $old_value['content'] )
371
+ return false;
372
+
373
+ return $this->set( $key, $new_value );
374
+ }
375
+
376
+ /**
377
+ * Use key as a counter and add integet value to it
378
+ */
379
+ public function counter_add( $key, $value ) {
380
+ if ( $value == 0 )
381
+ return true;
382
+
383
+ $fp = $this->fopen_write( $key, '', 'a' );
384
+ if ( !$fp )
385
+ return false;
386
+
387
+ // use "x" to store increment, since it's most often case
388
+ // and it will save 50% of size if only increments are used
389
+ if ( $value == 1 )
390
+ @fputs( $fp, 'x' );
391
+ else
392
+ @fputs( $fp, ' ' . (int)$value );
393
+
394
+ @fclose( $fp );
395
+ return true;
396
+ }
397
+
398
+ /**
399
+ * Use key as a counter and add integet value to it
400
+ */
401
+ public function counter_set( $key, $value ) {
402
+ $fp = $this->fopen_write( $key, '', 'wb' );
403
+ if ( !$fp )
404
+ return false;
405
+
406
+ $expire = W3TC_CACHE_FILE_EXPIRE_MAX;
407
+ $expires_at = time() + $expire;
408
+
409
+ @fputs( $fp, pack( 'L', $expires_at ) );
410
+ @fputs( $fp, '<?php exit; ?>' );
411
+ @fputs( $fp, (int)$value );
412
+ @fclose( $fp );
413
+
414
+ return true;
415
+ }
416
+
417
+ /**
418
+ * Get counter's value
419
+ */
420
+ public function counter_get( $key ) {
421
+ list( $value, $old_data ) = $this->_get_with_old_raw( $key );
422
+ if ( empty( $value ) )
423
+ return 0;
424
+
425
+ $original_length = strlen( $value );
426
+ $cut_value = str_replace( 'x', '', $value );
427
+
428
+ $count = $original_length - strlen( $cut_value );
429
+
430
+ // values more than 1 are stored as <space>value
431
+ $a = explode( ' ', $cut_value );
432
+ foreach ( $a as $counter_value )
433
+ $count += (int)$counter_value;
434
+
435
+ return $count;
436
+ }
437
+
438
+ private function fopen_write( $key, $group, $mode ) {
439
+ $storage_key = $this->get_item_key( $key );
440
+
441
+ $sub_path = $this->_get_path( $storage_key );
442
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR .
443
+ ( $group ? $group . DIRECTORY_SEPARATOR : '' ) . $sub_path;
444
+
445
+ $dir = dirname( $path );
446
+
447
+ if ( !@is_dir( $dir ) ) {
448
+ if ( !Util_File::mkdir_from( $dir, W3TC_CACHE_DIR ) )
449
+ return false;
450
+ }
451
+
452
+ $fp = @fopen( $path, $mode );
453
+ return $fp;
454
+ }
455
+ }
Cache_File_Cleaner.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * File cache cleaner class
6
+ */
7
+ class Cache_File_Cleaner {
8
+ /**
9
+ * Cache directory
10
+ *
11
+ * @var string
12
+ */
13
+ var $_cache_dir = '';
14
+
15
+ /**
16
+ * Clean operation time limit
17
+ *
18
+ * @var int
19
+ */
20
+ var $_clean_timelimit = 0;
21
+
22
+ /**
23
+ * Exclude files
24
+ *
25
+ * @var array
26
+ */
27
+ var $_exclude = array();
28
+
29
+ /**
30
+ * PHP5-style constructor
31
+ *
32
+ * @param array $config
33
+ */
34
+ function __construct( $config = array() ) {
35
+ $this->_cache_dir = ( isset( $config['cache_dir'] ) ? trim( $config['cache_dir'] ) : 'cache' );
36
+ $this->_clean_timelimit = ( isset( $config['clean_timelimit'] ) ? (int) $config['clean_timelimit'] : 180 );
37
+ $this->_exclude = ( isset( $config['exclude'] ) ? (array) $config['exclude'] : array() );
38
+ }
39
+
40
+ /**
41
+ * Run clean operation
42
+ *
43
+ * @return boolean
44
+ */
45
+ function clean() {
46
+ @set_time_limit( $this->_clean_timelimit );
47
+
48
+ $this->_clean( $this->_cache_dir, false );
49
+ }
50
+
51
+ /**
52
+ * Clean
53
+ *
54
+ * @param string $path
55
+ * @param bool $remove
56
+ * @return void
57
+ */
58
+ function _clean( $path, $remove = true ) {
59
+ $dir = @opendir( $path );
60
+
61
+ if ( $dir ) {
62
+ while ( ( $entry = @readdir( $dir ) ) !== false ) {
63
+ if ( $entry == '.' || $entry == '..' ) {
64
+ continue;
65
+ }
66
+
67
+ foreach ( $this->_exclude as $mask ) {
68
+ if ( fnmatch( $mask, basename( $entry ) ) ) {
69
+ continue 2;
70
+ }
71
+ }
72
+
73
+ $full_path = $path . DIRECTORY_SEPARATOR . $entry;
74
+
75
+ if ( @is_dir( $full_path ) ) {
76
+ $this->_clean( $full_path );
77
+ } elseif ( !$this->is_valid( $full_path ) ) {
78
+ @unlink( $full_path );
79
+ }
80
+ }
81
+
82
+ @closedir( $dir );
83
+
84
+ if ( $remove ) {
85
+ @rmdir( $path );
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Check if file is valid
92
+ *
93
+ * @param string $file
94
+ * @return bool
95
+ */
96
+ function is_valid( $file ) {
97
+ $valid = false;
98
+
99
+ if ( file_exists( $file ) ) {
100
+ $fp = @fopen( $file, 'rb' );
101
+
102
+ if ( $fp ) {
103
+ $expires = @fread( $fp, 4 );
104
+
105
+ if ( $expires !== false ) {
106
+ list( , $expires_at ) = @unpack( 'L', $expires );
107
+ $valid = ( time() < $expires_at );
108
+ }
109
+
110
+ @fclose( $fp );
111
+ }
112
+ }
113
+
114
+ return $valid;
115
+ }
116
+ }
Cache_File_Cleaner_Generic.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Generic file cache cleaner class
6
+ */
7
+ class Cache_File_Cleaner_Generic extends Cache_File_Cleaner {
8
+ /**
9
+ * Number of items processed
10
+ *
11
+ * @var integer
12
+ */
13
+ var $processed_count = 0;
14
+ /**
15
+ * Cache expire time
16
+ *
17
+ * @var int
18
+ */
19
+ var $_expire = 0;
20
+
21
+ /**
22
+ * PHP5-style constructor
23
+ *
24
+ * @param array $config
25
+ */
26
+ function __construct( $config = array() ) {
27
+ parent::__construct( $config );
28
+
29
+ $this->_expire = ( isset( $config['expire'] ) ? (int) $config['expire'] : 0 );
30
+
31
+ if ( !$this->_expire || $this->_expire > W3TC_CACHE_FILE_EXPIRE_MAX ) {
32
+ $this->_expire = 0;
33
+ }
34
+ }
35
+
36
+ function _clean( $path, $remove = false ) {
37
+ $dir = false;
38
+ if ( is_dir( $path ) ) {
39
+ $dir = @opendir( $path );
40
+ }
41
+
42
+ if ( $dir ) {
43
+ while ( ( $entry = @readdir( $dir ) ) !== false ) {
44
+ if ( $entry == '.' || $entry == '..' ) {
45
+ continue;
46
+ }
47
+
48
+ $full_path = $path . DIRECTORY_SEPARATOR . $entry;
49
+
50
+ if ( substr( $entry, -4 ) === '.old' && !$this->is_old_file_expired( $full_path ) ) {
51
+ continue;
52
+ }
53
+
54
+ foreach ( $this->_exclude as $mask ) {
55
+ if ( fnmatch( $mask, basename( $entry ) ) ) {
56
+ continue 2;
57
+ }
58
+ }
59
+
60
+
61
+ if ( @is_dir( $full_path ) ) {
62
+ $this->_clean( $full_path );
63
+ } elseif ( substr( $entry, -4 ) === '.old' ) {
64
+ $this->processed_count++;
65
+ @unlink( $full_path );
66
+ } elseif ( !$this->is_valid( $full_path ) ) {
67
+ $old_entry_path = $full_path . '.old';
68
+ $this->processed_count++;
69
+ if ( !@rename( $full_path, $old_entry_path ) ) {
70
+ // if we can delete old entry - do second attempt to store in old-entry file
71
+ if ( @unlink( $old_entry_path ) ) {
72
+ @rename( $full_path, $old_entry_path );
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ @closedir( $dir );
79
+ if ( $this->is_empty_dir( $path ) )
80
+ @rmdir( $path );
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Checks if file is valid
86
+ *
87
+ * @param string $file
88
+ * @return bool
89
+ */
90
+ function is_valid( $file ) {
91
+ if ( $this->_expire <= 0 )
92
+ return false;
93
+
94
+ if ( file_exists( $file ) ) {
95
+ $ftime = @filemtime( $file );
96
+
97
+ if ( $ftime && $ftime > ( time() - $this->_expire ) ) {
98
+ return true;
99
+ }
100
+ }
101
+
102
+ return false;
103
+ }
104
+
105
+ function is_old_file_expired( $file ) {
106
+ $ftime = @filemtime( $file );
107
+ $expire = $this->_expire ? $this->_expire * 5 : W3TC_CACHE_FILE_EXPIRE_MAX;
108
+ if ( $ftime && $ftime < ( time() - $expire ) ) {
109
+ return true;
110
+ }
111
+
112
+ return false;
113
+ }
114
+ function is_empty_dir( $dir ) {
115
+ return ( $files = @scandir( $dir ) ) && count( $files ) <= 2;
116
+ }
117
+ }
Cache_File_Generic.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Generic file cache
6
+ */
7
+
8
+
9
+
10
+ /**
11
+ * class Cache_File_Generic
12
+ */
13
+ class Cache_File_Generic extends Cache_File {
14
+ /**
15
+ * Expire
16
+ *
17
+ * @var integer
18
+ */
19
+ var $_expire = 0;
20
+
21
+ /**
22
+ * PHP5-style constructor
23
+ *
24
+ * @param array $config
25
+ */
26
+ function __construct( $config = array() ) {
27
+ parent::__construct( $config );
28
+
29
+ $this->_expire = ( isset( $config['expire'] ) ? (int) $config['expire'] : 0 );
30
+
31
+ if ( !$this->_expire || $this->_expire > W3TC_CACHE_FILE_EXPIRE_MAX ) {
32
+ $this->_expire = W3TC_CACHE_FILE_EXPIRE_MAX;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Sets data
38
+ *
39
+ * @param string $key
40
+ * @param string $var
41
+ * @param int $expire
42
+ * @param string $group Used to differentiate between groups of cache values
43
+ * @return boolean
44
+ */
45
+ function set( $key, $var, $expire = 0, $group = '' ) {
46
+ $key = $this->get_item_key( $key );
47
+ $sub_path = $this->_get_path( $key );
48
+ $path = $this->_cache_dir . '/' . $sub_path;
49
+
50
+ $dir = dirname( $path );
51
+
52
+ if ( !@is_dir( $dir ) ) {
53
+ if ( !Util_File::mkdir_from( $dir, W3TC_CACHE_DIR ) )
54
+ return false;
55
+ }
56
+
57
+ $tmppath = $path . '.' . getmypid();
58
+
59
+ $fp = @fopen( $tmppath, 'w' );
60
+ if ( !$fp )
61
+ return false;
62
+
63
+ if ( $this->_locking )
64
+ @flock( $fp, LOCK_EX );
65
+
66
+ @fputs( $fp, $var['content'] );
67
+ @fclose( $fp );
68
+
69
+ if ( $this->_locking )
70
+ @flock( $fp, LOCK_UN );
71
+
72
+ // some hostings create files with restrictive permissions
73
+ // not allowing apache to read it later
74
+ @chmod( $path, 0644 );
75
+
76
+ if ( @filesize( $tmppath ) > 0 ) {
77
+ @unlink( $path );
78
+ @rename( $tmppath, $path );
79
+ }
80
+
81
+ @unlink( $tmppath );
82
+
83
+ $old_entry_path = $path . '.old';
84
+ @unlink( $old_entry_path );
85
+
86
+ if ( Util_Environment::is_apache() && isset( $var['headers'] ) &&
87
+ isset( $var['headers']['Content-Type'] ) &&
88
+ substr( $var['headers']['Content-Type'], 0, 8 ) == 'text/xml' ) {
89
+ file_put_contents( dirname( $path ) . '/.htaccess',
90
+ "<IfModule mod_mime.c>\n" .
91
+ " RemoveType .html_gzip\n" .
92
+ " AddType text/xml .html_gzip\n" .
93
+ " RemoveType .html\n" .
94
+ " AddType text/xml .html\n".
95
+ "</IfModule>" );
96
+ }
97
+
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Returns data
103
+ *
104
+ * @param string $key
105
+ * @param string $group Used to differentiate between groups of cache values
106
+ * @return array
107
+ */
108
+ function get_with_old( $key, $group = '' ) {
109
+ $has_old_data = false;
110
+ $key = $this->get_item_key( $key );
111
+ $path = $this->_cache_dir . '/' . $this->_get_path( $key );
112
+
113
+ $data = $this->_read( $path );
114
+ if ( $data != null )
115
+ return array( $data, $has_old_data );
116
+
117
+
118
+ $path_old = $path . '.old';
119
+ $too_old_time = time() - 30;
120
+
121
+ if ( $exists = file_exists( $path_old ) ) {
122
+ $file_time = @filemtime( $path_old );
123
+ if ( $file_time ) {
124
+ if ( $file_time > $too_old_time ) {
125
+ // return old data
126
+ $has_old_data = true;
127
+ return array( $this->_read( $path_old ), $has_old_data );
128
+
129
+ }
130
+
131
+ @touch( $path_old );
132
+ }
133
+ }
134
+ $has_old_data = $exists;
135
+
136
+ return array( null, $has_old_data );
137
+ }
138
+
139
+ /**
140
+ * Reads file
141
+ *
142
+ * @param string $path
143
+ * @return array
144
+ */
145
+ private function _read( $path ) {
146
+ if ( !is_readable( $path ) )
147
+ return null;
148
+
149
+ $fp = @fopen( $path, 'r' );
150
+ if ( !$fp )
151
+ return null;
152
+
153
+ if ( $this->_locking )
154
+ @flock( $fp, LOCK_SH );
155
+
156
+ $var = '';
157
+
158
+ while ( !@feof( $fp ) )
159
+ $var .= @fread( $fp, 4096 );
160
+
161
+ @fclose( $fp );
162
+
163
+ if ( $this->_locking )
164
+ @flock( $fp, LOCK_UN );
165
+
166
+ return array(
167
+ '404' => false,
168
+ 'headers' => array(),
169
+ 'time' => null,
170
+ 'content' => $var
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Deletes data
176
+ *
177
+ * @param string $key
178
+ * @param string $group Used to differentiate between groups of cache values
179
+ * @return boolean
180
+ */
181
+ function delete( $key, $group = '' ) {
182
+ $key = $this->get_item_key( $key );
183
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR . $this->_get_path( $key );
184
+
185
+ if ( !file_exists( $path ) )
186
+ return true;
187
+
188
+ $old_entry_path = $path . '.old';
189
+ if ( @rename( $path, $old_entry_path ) )
190
+ return true;
191
+
192
+ // if we can delete old entry - do second attempt to store in old-entry file
193
+ if ( @unlink( $old_entry_path ) ) {
194
+ if ( @rename( $path, $old_entry_path ) )
195
+ return true;
196
+ }
197
+
198
+ return @unlink( $path );
199
+ }
200
+
201
+ /**
202
+ * Key to delete, deletes .old and primary if exists.
203
+ *
204
+ * @param unknown $key
205
+ * @return bool
206
+ */
207
+ function hard_delete( $key ) {
208
+ $key = $this->get_item_key( $key );
209
+ $path = $this->_cache_dir . DIRECTORY_SEPARATOR . $this->_get_path( $key );
210
+ $old_entry_path = $path . '.old';
211
+ @unlink( $old_entry_path );
212
+
213
+ if ( !file_exists( $path ) )
214
+ return true;
215
+ @unlink( $path );
216
+ return true;
217
+ }
218
+
219
+ /**
220
+ * Flushes all data
221
+ *
222
+ * @param string $group Used to differentiate between groups of cache values
223
+ * @return boolean
224
+ */
225
+ function flush( $group = '' ) {
226
+ if ( $group == 'sitemaps' ) {
227
+ $config = Dispatcher::config();
228
+ $sitemap_regex = $config->get_string( 'pgcache.purge.sitemap_regex' );
229
+ $this->_flush_based_on_regex( $sitemap_regex );
230
+ } else {
231
+ $c = new Cache_File_Cleaner_Generic( array(
232
+ 'cache_dir' => $this->_flush_dir,
233
+ 'exclude' => $this->_exclude,
234
+ 'clean_timelimit' => $this->_flush_timelimit
235
+ ) );
236
+
237
+ $c->clean();
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Returns cache file path by key
243
+ *
244
+ * @param string $key
245
+ * @return string
246
+ */
247
+ function _get_path( $key ) {
248
+ return $key;
249
+ }
250
+
251
+ function get_item_key( $key ) {
252
+ return $key;
253
+ }
254
+
255
+
256
+ /**
257
+ * Flush cache based on regex
258
+ *
259
+ * @param string $regex
260
+ */
261
+ private function _flush_based_on_regex( $regex ) {
262
+ if ( Util_Environment::is_wpmu() && !Util_Environment::is_wpmu_subdomain() ) {
263
+ $domain = get_home_url();
264
+ $parsed = parse_url( $domain );
265
+ $host = $parsed['host'];
266
+ $path = isset( $parsed['path'] ) ? '/' . trim( $parsed['path'], '/' ) : '';
267
+ $flush_dir = W3TC_CACHE_PAGE_ENHANCED_DIR . '/' . $host . $path;
268
+ } else
269
+ $flush_dir = W3TC_CACHE_PAGE_ENHANCED_DIR . '/' . Util_Environment::host();
270
+
271
+ $dir = @opendir( $flush_dir );
272
+ if ( $dir ) {
273
+ while ( ( $entry = @readdir( $dir ) ) !== false ) {
274
+ if ( $entry == '.' || $entry == '..' ) {
275
+ continue;
276
+ }
277
+ if ( preg_match( '/' . $regex . '/', basename( $entry ) ) ) {
278
+ Util_File::rmdir( $flush_dir . DIRECTORY_SEPARATOR . $entry );
279
+ }
280
+ }
281
+
282
+ @closedir( $dir );
283
+ }
284
+ }
285
+ }
Cache_Memcache.php ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * PECL Memcache class
6
+ */
7
+ class Cache_Memcache extends Cache_Base {
8
+ /**
9
+ * Memcache object
10
+ *
11
+ * @var Memcache
12
+ */
13
+ private $_memcache = null;
14
+
15
+ /*
16
+ * Used for faster flushing
17
+ *
18
+ * @var integer $_key_version
19
+ */
20
+ private $_key_version = array();
21
+
22
+ /**
23
+ * constructor
24
+ *
25
+ * @param array $config
26
+ */
27
+ function __construct( $config ) {
28
+ parent::__construct( $config );
29
+
30
+ $this->_memcache = new \Memcache();
31
+
32
+ if ( !empty( $config['servers'] ) ) {
33
+ $persistent = isset( $config['persistent'] ) ? (boolean) $config['persistent'] : false;
34
+
35
+ foreach ( (array) $config['servers'] as $server ) {
36
+ if ( substr( $server, 0, 5 ) == 'unix:' )
37
+ $this->_memcache->addServer( trim( $server ), 0, $persistent );
38
+ else {
39
+ list( $ip, $port ) = explode( ':', $server );
40
+ $this->_memcache->addServer( trim( $ip ), (integer) trim( $port ), $persistent );
41
+ }
42
+ }
43
+ } else {
44
+ return false;
45
+ }
46
+
47
+ return true;
48
+ }
49
+
50
+ /**
51
+ * Adds data
52
+ *
53
+ * @param string $key
54
+ * @param mixed $var
55
+ * @param integer $expire
56
+ * @param string $group Used to differentiate between groups of cache values
57
+ * @return boolean
58
+ */
59
+ function add( $key, &$var, $expire = 0, $group = '' ) {
60
+ return $this->set( $key, $var, $expire, $group );
61
+ }
62
+
63
+ /**
64
+ * Sets data
65
+ *
66
+ * @param string $key
67
+ * @param mixed $var
68
+ * @param integer $expire
69
+ * @param string $group Used to differentiate between groups of cache values
70
+ * @return boolean
71
+ */
72
+ function set( $key, $var, $expire = 0, $group = '' ) {
73
+ $var['key_version'] = $this->_get_key_version( $group );
74
+
75
+ $storage_key = $this->get_item_key( $key );
76
+ return @$this->_memcache->set( $storage_key, $var, false, $expire );
77
+ }
78
+
79
+ /**
80
+ * Returns data
81
+ *
82
+ * @param string $key
83
+ * @param string $group Used to differentiate between groups of cache values
84
+ * @return mixed
85
+ */
86
+ function get_with_old( $key, $group = '' ) {
87
+ $has_old_data = false;
88
+
89
+ $storage_key = $this->get_item_key( $key );
90
+
91
+ $v = @$this->_memcache->get( $storage_key );
92
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
93
+ return array( null, $has_old_data );
94
+
95
+ $key_version = $this->_get_key_version( $group );
96
+ if ( $v['key_version'] == $key_version )
97
+ return array( $v, $has_old_data );
98
+
99
+ if ( $v['key_version'] > $key_version ) {
100
+ $this->_set_key_version( $v['key_version'], $group );
101
+ return array( $v, $has_old_data );
102
+ }
103
+
104
+ // key version is old
105
+ if ( !$this->_use_expired_data )
106
+ return array( null, $has_old_data );
107
+
108
+ // if we have expired data - update it for future use and let
109
+ // current process recalculate it
110
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
111
+ if ( $expires_at == null || time() > $expires_at ) {
112
+ $v['expires_at'] = time() + 30;
113
+ @$this->_memcache->set( $storage_key, $v, false, 0 );
114
+ $has_old_data = true;
115
+
116
+ return array( null, $has_old_data );
117
+ }
118
+
119
+ // return old version
120
+ return array( $v, $has_old_data );
121
+ }
122
+
123
+ /**
124
+ * Replaces data
125
+ *
126
+ * @param string $key
127
+ * @param mixed $var
128
+ * @param integer $expire
129
+ * @param string $group Used to differentiate between groups of cache values
130
+ * @return boolean
131
+ */
132
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
133
+ return $this->set( $key, $var, $expire, $group );
134
+ }
135
+
136
+ /**
137
+ * Deletes data
138
+ *
139
+ * @param string $key
140
+ * @param string $group
141
+ * @return boolean
142
+ */
143
+ function delete( $key, $group = '' ) {
144
+ $storage_key = $this->get_item_key( $key );
145
+
146
+ if ( $this->_use_expired_data ) {
147
+ $v = @$this->_memcache->get( $storage_key );
148
+ if ( is_array( $v ) ) {
149
+ $v['key_version'] = 0;
150
+ @$this->_memcache->set( $storage_key, $v, false, 0 );
151
+ return true;
152
+ }
153
+ }
154
+ return @$this->_memcache->delete( $storage_key, 0 );
155
+ }
156
+
157
+ /**
158
+ * Key to delete, deletes .old and primary if exists.
159
+ *
160
+ * @param unknown $key
161
+ * @return bool
162
+ */
163
+ function hard_delete( $key ) {
164
+ $storage_key = $this->get_item_key( $key );
165
+ return @$this->_memcache->delete( $storage_key, 0 );
166
+ }
167
+
168
+ /**
169
+ * Flushes all data
170
+ *
171
+ * @param string $group Used to differentiate between groups of cache values
172
+ * @return boolean
173
+ */
174
+ function flush( $group = '' ) {
175
+ $this->_get_key_version( $group ); // initialize $this->_key_version
176
+ $this->_key_version[$group]++;
177
+ $this->_set_key_version( $this->_key_version[$group], $group );
178
+ return true;
179
+ }
180
+
181
+ /**
182
+ * Checks if engine can function properly in this environment
183
+ *
184
+ * @return bool
185
+ */
186
+ public function available() {
187
+ return class_exists( 'Memcache' );
188
+ }
189
+
190
+ public function get_statistics() {
191
+ return $this->_memcache->getStats();
192
+ }
193
+
194
+ /**
195
+ * Returns key version
196
+ *
197
+ * @param string $group Used to differentiate between groups of cache values
198
+ * @return integer
199
+ */
200
+ private function _get_key_version( $group = '' ) {
201
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
202
+ $v = @$this->_memcache->get( $this->_get_key_version_key( $group ) );
203
+ $v = intval( $v );
204
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
205
+ }
206
+
207
+ return $this->_key_version[$group];
208
+ }
209
+
210
+ /**
211
+ * Sets new key version
212
+ *
213
+ * @param unknown $v
214
+ * @param string $group Used to differentiate between groups of cache values
215
+ * @return boolean
216
+ */
217
+ private function _set_key_version( $v, $group = '' ) {
218
+ @$this->_memcache->set( $this->_get_key_version_key( $group ), $v, false, 0 );
219
+ }
220
+
221
+ /**
222
+ * Returns size used by cache
223
+ */
224
+ public function get_stats_size( $timeout_time ) {
225
+ $size = array(
226
+ 'bytes' => 0,
227
+ 'items' => 0,
228
+ 'timeout_occurred' => false
229
+ );
230
+
231
+ $key_prefix = $this->get_item_key( '' );
232
+
233
+ $slabs = @$this->_memcache->getExtendedStats( 'slabs' );
234
+ $slabs_plain = array();
235
+
236
+ if ( is_array( $slabs ) ) {
237
+ foreach ( $slabs as $server => $server_slabs ) {
238
+ foreach ( $server_slabs as $slab_id => $slab_meta ) {
239
+ if ( (int)$slab_id > 0 )
240
+ $slabs_plain[(int)$slab_id] = '*';
241
+ }
242
+ }
243
+ }
244
+
245
+ foreach ( $slabs_plain as $slab_id => $nothing ) {
246
+ $cdump = @$this->_memcache->getExtendedStats( 'cachedump',
247
+ (int)$slab_id );
248
+ if ( !is_array( $cdump ) )
249
+ continue;
250
+
251
+ foreach ( $cdump as $server => $keys_data ) {
252
+ if ( !is_array( $keys_data ) )
253
+ continue;
254
+
255
+ foreach ( $keys_data as $key => $size_expiration ) {
256
+ if ( substr( $key, 0, strlen( $key_prefix ) ) == $key_prefix ) {
257
+ if ( count( $size_expiration ) > 0 ) {
258
+ $size['bytes'] += $size_expiration[0];
259
+ $size['items']++;
260
+ }
261
+ }
262
+
263
+ }
264
+ }
265
+ }
266
+
267
+ return $size;
268
+ }
269
+
270
+ /**
271
+ * Used to replace as atomically as possible known value to new one
272
+ */
273
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
274
+ // cant guarantee atomic action here, memcache doesnt support CAS
275
+ $value = $this->get( $key );
276
+ if ( isset( $old_value['content'] ) &&
277
+ $value['content'] != $old_value['content'] )
278
+ return false;
279
+
280
+ return $this->set( $key, $new_value );
281
+ }
282
+
283
+ /**
284
+ * Use key as a counter and add integet value to it
285
+ */
286
+ public function counter_add( $key, $value ) {
287
+ if ( $value == 0 )
288
+ return true;
289
+
290
+ $storage_key = $this->get_item_key( $key );
291
+ $r = @$this->_memcache->increment( $storage_key, $value );
292
+ if ( !$r ) // it doesnt initialize counter by itself
293
+ $this->counter_set( $key, 0 );
294
+
295
+ return $r;
296
+ }
297
+
298
+ /**
299
+ * Use key as a counter and add integet value to it
300
+ */
301
+ public function counter_set( $key, $value ) {
302
+ $storage_key = $this->get_item_key( $key );
303
+ return @$this->_memcache->set( $storage_key, $value );
304
+ }
305
+
306
+ /**
307
+ * Get counter's value
308
+ */
309
+ public function counter_get( $key ) {
310
+ $storage_key = $this->get_item_key( $key );
311
+ $v = (int)@$this->_memcache->get( $storage_key );
312
+
313
+ return $v;
314
+ }
315
+ }
Cache_Memcached.php ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * PECL Memcached class
6
+ */
7
+ class Cache_Memcached extends Cache_Base {
8
+ /**
9
+ * Memcache object
10
+ *
11
+ * @var Memcache
12
+ */
13
+ private $_memcache = null;
14
+
15
+ /*
16
+ * Used for faster flushing
17
+ *
18
+ * @var integer $_key_version
19
+ */
20
+ private $_key_version = array();
21
+
22
+ /*
23
+ * Configuration used to reinitialize persistent object
24
+ *
25
+ * @var integer $_key_version
26
+ */
27
+ private $_config = null;
28
+
29
+ /**
30
+ * constructor
31
+ *
32
+ * @param array $config
33
+ */
34
+ function __construct( $config ) {
35
+ parent::__construct( $config );
36
+
37
+ if ( isset( $config['persistent'] ) && $config['persistent'] ) {
38
+ $this->_config = $config;
39
+ $this->_memcache = new \Memcached( $this->_get_key_version_key( '' ) );
40
+ $server_list = $this->_memcache->getServerList();
41
+
42
+ if ( empty( $server_list ) )
43
+ return $this->initialize( $config );
44
+ else
45
+ return true;
46
+ } else {
47
+ $this->_memcache = new \Memcached();
48
+ return $this->initialize( $config );
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Initializes
54
+ */
55
+ private function initialize( $config ) {
56
+ if ( empty( $config['servers'] ) )
57
+ return false;
58
+
59
+ if ( defined( '\Memcached::OPT_REMOVE_FAILED_SERVERS' ) ) {
60
+ $this->_memcache->setOption( \Memcached::OPT_REMOVE_FAILED_SERVERS, true );
61
+ }
62
+
63
+ if ( isset( $config['aws_autodiscovery'] ) &&
64
+ $config['aws_autodiscovery'] &&
65
+ defined( '\Memcached::OPT_CLIENT_MODE' ) &&
66
+ defined( '\Memcached::DYNAMIC_CLIENT_MODE' ) )
67
+ $this->_memcache->setOption( \Memcached::OPT_CLIENT_MODE,
68
+ \Memcached::DYNAMIC_CLIENT_MODE );
69
+
70
+ foreach ( (array)$config['servers'] as $server ) {
71
+ if ( substr( $server, 0, 5 ) == 'unix:' )
72
+ $this->_memcache->addServer( trim( $server ), 0 );
73
+ else {
74
+ list( $ip, $port ) = explode( ':', $server );
75
+ $this->_memcache->addServer( trim( $ip ), (integer) trim( $port ) );
76
+ }
77
+ }
78
+
79
+ if ( isset( $config['username'] ) && !empty( $config['username'] ) &&
80
+ method_exists( $this->_memcache, 'setSaslAuthData' ) &&
81
+ ini_get( 'memcached.use_sasl' ) )
82
+ $this->_memcache->setSaslAuthData( $config['username'],
83
+ $config['password'] );
84
+
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * Adds data
90
+ *
91
+ * @param string $key
92
+ * @param mixed $var
93
+ * @param integer $expire
94
+ * @param string $group Used to differentiate between groups of cache values
95
+ * @return boolean
96
+ */
97
+ function add( $key, &$var, $expire = 0, $group = '' ) {
98
+ return $this->set( $key, $var, $expire, $group );
99
+ }
100
+
101
+ /**
102
+ * Sets data
103
+ *
104
+ * @param string $key
105
+ * @param mixed $var
106
+ * @param integer $expire
107
+ * @param string $group Used to differentiate between groups of cache values
108
+ * @return boolean
109
+ */
110
+ function set( $key, $var, $expire = 0, $group = '' ) {
111
+ $var['key_version'] = $this->_get_key_version( $group );
112
+
113
+ $storage_key = $this->get_item_key( $key );
114
+ return @$this->_memcache->set( $storage_key, $var, $expire );
115
+ }
116
+
117
+ /**
118
+ * Returns data
119
+ *
120
+ * @param string $key
121
+ * @param string $group Used to differentiate between groups of cache values
122
+ * @return mixed
123
+ */
124
+ function get_with_old( $key, $group = '' ) {
125
+ $has_old_data = false;
126
+
127
+ $storage_key = $this->get_item_key( $key );
128
+
129
+ $v = @$this->_memcache->get( $storage_key );
130
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
131
+ return array( null, $has_old_data );
132
+
133
+ $key_version = $this->_get_key_version( $group );
134
+ if ( $v['key_version'] == $key_version )
135
+ return array( $v, $has_old_data );
136
+
137
+ if ( $v['key_version'] > $key_version ) {
138
+ $this->_set_key_version( $v['key_version'], $group );
139
+ return array( $v, $has_old_data );
140
+ }
141
+
142
+ // key version is old
143
+ if ( !$this->_use_expired_data )
144
+ return array( null, $has_old_data );
145
+
146
+ // if we have expired data - update it for future use and let
147
+ // current process recalculate it
148
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
149
+ if ( $expires_at == null || time() > $expires_at ) {
150
+ $v['expires_at'] = time() + 30;
151
+ @$this->_memcache->set( $storage_key, $v, 0 );
152
+ $has_old_data = true;
153
+
154
+ return array( null, $has_old_data );
155
+ }
156
+
157
+ // return old version
158
+ return array( $v, $has_old_data );
159
+ }
160
+
161
+ /**
162
+ * Replaces data
163
+ *
164
+ * @param string $key
165
+ * @param mixed $var
166
+ * @param integer $expire
167
+ * @param string $group Used to differentiate between groups of cache values
168
+ * @return boolean
169
+ */
170
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
171
+ return $this->set( $key, $var, $expire, $group );
172
+ }
173
+
174
+ /**
175
+ * Deletes data
176
+ *
177
+ * @param string $key
178
+ * @param string $group
179
+ * @return boolean
180
+ */
181
+ function delete( $key, $group = '' ) {
182
+ $storage_key = $this->get_item_key( $key );
183
+
184
+ if ( $this->_use_expired_data ) {
185
+ $v = @$this->_memcache->get( $storage_key );
186
+ if ( is_array( $v ) ) {
187
+ $v['key_version'] = 0;
188
+ @$this->_memcache->set( $storage_key, $v, 0 );
189
+ return true;
190
+ }
191
+ }
192
+ return @$this->_memcache->delete( $storage_key );
193
+ }
194
+
195
+ /**
196
+ * Key to delete, deletes .old and primary if exists.
197
+ *
198
+ * @param unknown $key
199
+ * @return bool
200
+ */
201
+ function hard_delete( $key ) {
202
+ $storage_key = $this->get_item_key( $key );
203
+ return @$this->_memcache->delete( $storage_key );
204
+ }
205
+
206
+ /**
207
+ * Flushes all data
208
+ *
209
+ * @param string $group Used to differentiate between groups of cache values
210
+ * @return boolean
211
+ */
212
+ function flush( $group = '' ) {
213
+ $this->_get_key_version( $group ); // initialize $this->_key_version
214
+ $this->_key_version[$group]++;
215
+ $this->_set_key_version( $this->_key_version[$group], $group );
216
+
217
+ // for persistent connections - apply new config to the object
218
+ // otherwise it will keep old servers list
219
+ if ( !is_null( $this->_config ) ) {
220
+ if ( method_exists( $this->_memcache, 'resetServerList' ) )
221
+ $this->_memcache->resetServerList();
222
+
223
+ $this->initialize( $this->_config );
224
+ }
225
+
226
+ return true;
227
+ }
228
+
229
+ /**
230
+ * Checks if engine can function properly in this environment
231
+ *
232
+ * @return bool
233
+ */
234
+ public function available() {
235
+ return class_exists( 'Memcached' );
236
+ }
237
+
238
+ public function get_statistics() {
239
+ $a = $this->_memcache->getStats();
240
+ if ( count( $a ) > 0 ) {
241
+ $keys = array_keys( $a );
242
+ $key = $keys[0];
243
+ return $a[$key];
244
+ }
245
+
246
+ return $a;
247
+ }
248
+
249
+ /**
250
+ * Returns key version
251
+ *
252
+ * @param string $group Used to differentiate between groups of cache values
253
+ * @return integer
254
+ */
255
+ private function _get_key_version( $group = '' ) {
256
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
257
+ $v = @$this->_memcache->get( $this->_get_key_version_key( $group ) );
258
+ $v = intval( $v );
259
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
260
+ }
261
+
262
+ return $this->_key_version[$group];
263
+ }
264
+
265
+ /**
266
+ * Sets new key version
267
+ *
268
+ * @param unknown $v
269
+ * @param string $group Used to differentiate between groups of cache values
270
+ * @return boolean
271
+ */
272
+ private function _set_key_version( $v, $group = '' ) {
273
+ @$this->_memcache->set( $this->_get_key_version_key( $group ), $v, 0 );
274
+ }
275
+
276
+
277
+ /**
278
+ * Returns size used by cache
279
+ */
280
+ public function get_stats_size( $timeout_time ) {
281
+ $size = array(
282
+ 'bytes' => 0,
283
+ 'items' => 0,
284
+ 'timeout_occurred' => false,
285
+ );
286
+
287
+ $key_prefix = $this->get_item_key( '' );
288
+ $error_occurred = false;
289
+
290
+ $server_list = $this->_memcache->getServerList();
291
+ $n = 0;
292
+
293
+ foreach ( $server_list as $server ) {
294
+ $loader = new Cache_Memcached_Stats( $server['host'], $server['port'] );
295
+ $slabs = $loader->slabs();
296
+ if ( !is_array( $slabs ) ) {
297
+ $error_occurred = true;
298
+ continue;
299
+ }
300
+
301
+ foreach ( $slabs as $slab_id ) {
302
+ $cdump = $loader->cachedump( $slab_id );
303
+ if ( !is_array( $cdump ) )
304
+ continue;
305
+
306
+ foreach ( $cdump as $line ) {
307
+ $key_data = explode( ' ', $line );
308
+ if ( !is_array( $key_data ) || count( $key_data ) < 3 )
309
+ continue;
310
+ $n++;
311
+ if ( $n % 10 == 0 ) {
312
+ $size['timeout_occurred'] = ( time() > $timeout_time );
313
+ if ( $size['timeout_occurred'] )
314
+ return $size;
315
+ }
316
+
317
+ $key = $key_data[1];
318
+ $bytes = substr( $key_data[2], 1 );
319
+
320
+ if ( substr( $key, 0, strlen( $key_prefix ) ) == $key_prefix ) {
321
+ $size['bytes'] += $bytes;
322
+ $size['items']++;
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ if ( $error_occurred && $size['items'] <= 0 ) {
329
+ $size['bytes'] = null;
330
+ $size['items'] = null;
331
+ }
332
+
333
+ return $size;
334
+ }
335
+
336
+ /**
337
+ * Used to replace as atomically as possible known value to new one
338
+ */
339
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
340
+ $storage_key = $this->get_item_key( $key );
341
+
342
+ $cas = null;
343
+ $value = @$this->_memcache->get( $storage_key, null, $cas );
344
+
345
+ if ( !is_array( $value ) )
346
+ return false;
347
+
348
+ if ( isset( $old_value['content'] ) &&
349
+ $value['content'] != $old_value['content'] )
350
+ return false;
351
+
352
+ return @$this->_memcache->cas( $cas, $storage_key, $new_value );
353
+ }
354
+
355
+ /**
356
+ * Use key as a counter and add integet value to it
357
+ */
358
+ public function counter_add( $key, $value ) {
359
+ if ( $value == 0 )
360
+ return true;
361
+
362
+ $storage_key = $this->get_item_key( $key );
363
+ $r = @$this->_memcache->increment( $storage_key, $value );
364
+ if ( !$r ) // it doesnt initialize counter by itself
365
+ $this->counter_set( $key, 0 );
366
+
367
+ return $r;
368
+ }
369
+
370
+ /**
371
+ * Use key as a counter and add integet value to it
372
+ */
373
+ public function counter_set( $key, $value ) {
374
+ $storage_key = $this->get_item_key( $key );
375
+ return @$this->_memcache->set( $storage_key, $value );
376
+ }
377
+
378
+ /**
379
+ * Get counter's value
380
+ */
381
+ public function counter_get( $key ) {
382
+ $storage_key = $this->get_item_key( $key );
383
+ $v = (int)@$this->_memcache->get( $storage_key );
384
+
385
+ return $v;
386
+ }
387
+ }
Cache_Memcached_Stats.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Download extended statistics since module cant do it by itself
6
+ */
7
+ class Cache_Memcached_Stats {
8
+ public function __construct( $host, $port ) {
9
+ $this->host = $host;
10
+ $this->port = $port;
11
+ }
12
+
13
+ public function request( $command ) {
14
+ $handle = @fsockopen( $this->host, $this->port );
15
+ if ( !$handle )
16
+ return null;
17
+
18
+ fwrite( $handle, $command . "\r\n" );
19
+
20
+ $response = array();
21
+ while ( ( !feof( $handle ) ) ) {
22
+ $line = fgets( $handle );
23
+ $response[] = $line;
24
+
25
+ if ( $this->end( $line, $command ) )
26
+ break;
27
+ }
28
+
29
+ @fclose( $handle );
30
+ return $response;
31
+ }
32
+
33
+ private function end( $buffer, $command ) {
34
+ // incr or decr also return integer
35
+ if ( ( preg_match( '/^(incr|decr)/', $command ) ) ) {
36
+ if ( preg_match(
37
+ '/^(END|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|[0-9]*)/',
38
+ $buffer ) )
39
+ return true;
40
+ } else {
41
+ if ( preg_match( '/^(END|DELETED|OK|ERROR|SERVER_ERROR|CLIENT_ERROR|NOT_FOUND|STORED|RESET|TOUCHED)/', $buffer ) )
42
+ return true;
43
+ }
44
+
45
+ return false;
46
+ }
47
+
48
+ public function parse( $lines ) {
49
+ $return = array();
50
+
51
+ foreach ( $lines as $line ) {
52
+ $data = explode( ' ', $line );
53
+ $return[] = $data;
54
+ }
55
+
56
+ return $return;
57
+ }
58
+
59
+ public function slabs() {
60
+ $result = $this->request( 'stats slabs' );
61
+ if ( is_null( $result ) )
62
+ return null;
63
+
64
+ $result = $this->parse( $result );
65
+ $slabs = array();
66
+
67
+ foreach ( $result as $line_words ) {
68
+ if ( count( $line_words ) < 2 )
69
+ continue;
70
+
71
+ $key = explode( ':', $line_words[1] );
72
+ if ( (int)$key[0] > 0 )
73
+ $slabs[$key[0]] = '*';
74
+ }
75
+
76
+ return array_keys( $slabs );
77
+ }
78
+
79
+ public function cachedump( $slab_id ) {
80
+ $result = $this->request( 'stats cachedump ' . $slab_id . ' 0' );
81
+ if ( is_null( $result ) )
82
+ return null;
83
+
84
+ // return pure data to limit memory usage
85
+ return $result;
86
+ }
87
+ }
Cache_Redis.php ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Redis cache engine
6
+ */
7
+ class Cache_Redis extends Cache_Base {
8
+ private $_accessors = array();
9
+ private $_key_version = array();
10
+
11
+ private $_persistent;
12
+ private $_password;
13
+ private $_servers;
14
+ private $_dbid;
15
+
16
+ /**
17
+ * constructor
18
+ *
19
+ * @param array $config
20
+ */
21
+ function __construct( $config ) {
22
+ parent::__construct( $config );
23
+
24
+ $this->_persistent = ( isset( $config['persistent'] ) && $config['persistent'] );
25
+ $this->_servers = (array)$config['servers'];
26
+ $this->_password = $config['password'];
27
+ $this->_dbid = $config['dbid'];
28
+ }
29
+
30
+ /**
31
+ * Adds data
32
+ *
33
+ * @param string $key
34
+ * @param mixed $var
35
+ * @param integer $expire
36
+ * @param string $group Used to differentiate between groups of cache values
37
+ * @return boolean
38
+ */
39
+ function add( $key, &$var, $expire = 0, $group = '' ) {
40
+ return $this->set( $key, $var, $expire, $group );
41
+ }
42
+
43
+ /**
44
+ * Sets data
45
+ *
46
+ * @param string $key
47
+ * @param mixed $var
48
+ * @param integer $expire
49
+ * @param string $group Used to differentiate between groups of cache values
50
+ * @return boolean
51
+ */
52
+ function set( $key, $value, $expire = 0, $group = '' ) {
53
+ $value['key_version'] = $this->_get_key_version( $group );
54
+
55
+ $storage_key = $this->get_item_key( $key );
56
+ $accessor = $this->_get_accessor( $storage_key );
57
+ if ( is_null( $accessor ) )
58
+ return false;
59
+
60
+ return $accessor->setex( $storage_key, $expire, serialize( $value ) );
61
+ }
62
+
63
+ /**
64
+ * Returns data
65
+ *
66
+ * @param string $key
67
+ * @param string $group Used to differentiate between groups of cache values
68
+ * @return mixed
69
+ */
70
+ function get_with_old( $key, $group = '' ) {
71
+ $has_old_data = false;
72
+
73
+ $storage_key = $this->get_item_key( $key );
74
+ $accessor = $this->_get_accessor( $storage_key );
75
+ if ( is_null( $accessor ) )
76
+ return array( null, false );
77
+
78
+ $v = $accessor->get( $storage_key );
79
+ $v = @unserialize( $v );
80
+
81
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
82
+ return array( null, $has_old_data );
83
+
84
+ $key_version = $this->_get_key_version( $group );
85
+ if ( $v['key_version'] == $key_version )
86
+ return array( $v, $has_old_data );
87
+
88
+ if ( $v['key_version'] > $key_version ) {
89
+ $this->_set_key_version( $v['key_version'], $group );
90
+ return array( $v, $has_old_data );
91
+ }
92
+
93
+ // key version is old
94
+ if ( !$this->_use_expired_data )
95
+ return array( null, $has_old_data );
96
+
97
+ // if we have expired data - update it for future use and let
98
+ // current process recalculate it
99
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
100
+ if ( $expires_at == null || time() > $expires_at ) {
101
+ $v['expires_at'] = time() + 30;
102
+ $accessor->setex( $storage_key, 60, serialize( $v ) );
103
+ $has_old_data = true;
104
+
105
+ return array( null, $has_old_data );
106
+ }
107
+
108
+ // return old version
109
+ return array( $v, $has_old_data );
110
+ }
111
+
112
+ /**
113
+ * Replaces data
114
+ *
115
+ * @param string $key
116
+ * @param mixed $var
117
+ * @param integer $expire
118
+ * @param string $group Used to differentiate between groups of cache values
119
+ * @return boolean
120
+ */
121
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
122
+ return $this->set( $key, $var, $expire, $group );
123
+ }
124
+
125
+ /**
126
+ * Deletes data
127
+ *
128
+ * @param string $key
129
+ * @param string $group
130
+ * @return boolean
131
+ */
132
+ function delete( $key, $group = '' ) {
133
+ $storage_key = $this->get_item_key( $key );
134
+ $accessor = $this->_get_accessor( $storage_key );
135
+ if ( is_null( $accessor ) )
136
+ return false;
137
+
138
+ if ( $this->_use_expired_data ) {
139
+ $v = $accessor->get( $storage_key );
140
+ $ttl = $accessor->ttl( $storage_key );
141
+ if ( is_array( $v ) ) {
142
+ $v['key_version'] = 0;
143
+ $accessor->setex( $storage_key, $ttl, $v );
144
+ return true;
145
+ }
146
+ }
147
+ return $accessor->delete( $storage_key );
148
+ }
149
+
150
+ /**
151
+ * Key to delete, deletes .old and primary if exists.
152
+ *
153
+ * @param unknown $key
154
+ * @return bool
155
+ */
156
+ function hard_delete( $key ) {
157
+ $storage_key = $this->get_item_key( $key );
158
+ $accessor = $this->_get_accessor( $storage_key );
159
+ if ( is_null( $accessor ) )
160
+ return false;
161
+
162
+ return $accessor->delete( $storage_key );
163
+ }
164
+
165
+ /**
166
+ * Flushes all data
167
+ *
168
+ * @param string $group Used to differentiate between groups of cache values
169
+ * @return boolean
170
+ */
171
+ function flush( $group = '' ) {
172
+ $this->_get_key_version( $group ); // initialize $this->_key_version
173
+ $this->_key_version[$group]++;
174
+ $this->_set_key_version( $this->_key_version[$group], $group );
175
+
176
+ return true;
177
+ }
178
+
179
+ /**
180
+ * Checks if engine can function properly in this environment
181
+ *
182
+ * @return bool
183
+ */
184
+ public function available() {
185
+ return class_exists( 'Redis' );
186
+ }
187
+
188
+ public function get_statistics() {
189
+ $accessor = $this->_get_accessor( '' ); // single-server mode used for stats
190
+ if ( is_null( $accessor ) )
191
+ return array();
192
+
193
+ $a = $accessor->info();
194
+
195
+ return $a;
196
+ }
197
+
198
+ /**
199
+ * Returns key version
200
+ *
201
+ * @param string $group Used to differentiate between groups of cache values
202
+ * @return integer
203
+ */
204
+ private function _get_key_version( $group = '' ) {
205
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
206
+ $storage_key = $this->_get_key_version_key( $group );
207
+ $accessor = $this->_get_accessor( $storage_key );
208
+ if ( is_null( $accessor ) )
209
+ return 0;
210
+
211
+ $v = $accessor->get( $storage_key );
212
+ $v = intval( $v );
213
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
214
+ }
215
+
216
+ return $this->_key_version[$group];
217
+ }
218
+
219
+ /**
220
+ * Sets new key version
221
+ *
222
+ * @param unknown $v
223
+ * @param string $group Used to differentiate between groups of cache values
224
+ * @return boolean
225
+ */
226
+ private function _set_key_version( $v, $group = '' ) {
227
+ $storage_key = $this->_get_key_version_key( $group );
228
+ $accessor = $this->_get_accessor( $storage_key );
229
+ if ( is_null( $accessor ) )
230
+ return false;
231
+
232
+ $accessor->set( $storage_key, $v );
233
+ return true;
234
+ }
235
+
236
+ /**
237
+ * Used to replace as atomically as possible known value to new one
238
+ */
239
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
240
+ $storage_key = $this->get_item_key( $key );
241
+ $accessor = $this->_get_accessor( $storage_key );
242
+ if ( is_null( $accessor ) )
243
+ return false;
244
+
245
+ $accessor->watch( $storage_key );
246
+
247
+ $value = $accessor->get( $storage_key );
248
+ if ( !is_array( $value ) ) {
249
+ $accessor->unwatch();
250
+ return false;
251
+ }
252
+
253
+ if ( isset( $old_value['content'] ) &&
254
+ $value['content'] != $old_value['content'] ) {
255
+ $accessor->unwatch();
256
+ return false;
257
+ }
258
+
259
+ return $ret = $accessor->multi()
260
+ ->set( $storage_key, $new_value )
261
+ ->exec();
262
+ }
263
+
264
+ /**
265
+ * Use key as a counter and add integet value to it
266
+ */
267
+ public function counter_add( $key, $value ) {
268
+ if ( $value == 0 )
269
+ return true;
270
+
271
+ $storage_key = $this->get_item_key( $key );
272
+ $accessor = $this->_get_accessor( $storage_key );
273
+ if ( is_null( $accessor ) )
274
+ return false;
275
+
276
+ $r = $accessor->incrBy( $storage_key, $value );
277
+ if ( !$r ) // it doesnt initialize counter by itself
278
+ $this->counter_set( $key, 0 );
279
+
280
+ return $r;
281
+ }
282
+
283
+ /**
284
+ * Use key as a counter and add integet value to it
285
+ */
286
+ public function counter_set( $key, $value ) {
287
+ $storage_key = $this->get_item_key( $key );
288
+ $accessor = $this->_get_accessor( $storage_key );
289
+ if ( is_null( $accessor ) )
290
+ return false;
291
+
292
+ return $accessor->set( $storage_key, $value );
293
+ }
294
+
295
+ /**
296
+ * Get counter's value
297
+ */
298
+ public function counter_get( $key ) {
299
+ $storage_key = $this->get_item_key( $key );
300
+ $accessor = $this->_get_accessor( $storage_key );
301
+ if ( is_null( $accessor ) )
302
+ return 0;
303
+
304
+ $v = (int)$accessor->get( $storage_key );
305
+
306
+ return $v;
307
+ }
308
+
309
+ private function _get_accessor( $key ) {
310
+ if ( count( $this->_servers ) <= 1 )
311
+ $index = 0;
312
+ else {
313
+ $index = crc32( $key ) % count( $this->_servers );
314
+ }
315
+
316
+ if ( isset( $this->_accessors[$index] ) )
317
+ return $this->_accessors[$index];
318
+
319
+ if ( !isset( $this->_servers[$index] ) )
320
+ $this->_accessors[$index] = null;
321
+ else {
322
+ try {
323
+ $server = $this->_servers[$index];
324
+ $accessor = new \Redis();
325
+
326
+ if ( substr( $server, 0, 5 ) == 'unix:' ) {
327
+ if ( $this->_persistent )
328
+ $accessor->pconnect( trim( substr( $server, 5 ) ) );
329
+ else
330
+ $accessor->connect( trim( substr( $server, 5 ) ) );
331
+ } else {
332
+ list( $ip, $port ) = explode( ':', $server );
333
+
334
+ if ( $this->_persistent )
335
+ $accessor->pconnect( trim( $ip ), (integer) trim( $port ) );
336
+ else
337
+ $accessor->connect( trim( $ip ), (integer) trim( $port ) );
338
+ }
339
+
340
+ if ( !empty( $this->_password ) )
341
+ $accessor->auth( $this->_password );
342
+ $accessor->select( $this->_dbid );
343
+ } catch ( \Exception $e ) {
344
+ error_log( $e->getMessage() );
345
+ $accessor = null;
346
+ }
347
+
348
+ $this->_accessors[$index] = $accessor;
349
+ }
350
+
351
+ return $this->_accessors[$index];
352
+ }
353
+ }
Cache_Wincache.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Wincache class
6
+ */
7
+ class Cache_Wincache extends Cache_Base {
8
+
9
+ /*
10
+ * Used for faster flushing
11
+ *
12
+ * @var integer $_key_version
13
+ */
14
+ private $_key_version = array();
15
+
16
+ /**
17
+ * Adds data
18
+ *
19
+ * @param string $key
20
+ * @param mixed $var
21
+ * @param integer $expire
22
+ * @param string $group Used to differentiate between groups of cache values
23
+ * @return boolean
24
+ */
25
+ function add( $key, &$var, $expire = 0, $group = '' ) {
26
+ return $this->set( $key, $var, $expire, $group );
27
+ }
28
+
29
+ /**
30
+ * Sets data
31
+ *
32
+ * @param string $key
33
+ * @param mixed $var
34
+ * @param integer $expire
35
+ * @param string $group Used to differentiate between groups of cache values
36
+ * @return boolean
37
+ */
38
+ function set( $key, $var, $expire = 0, $group = '' ) {
39
+ $var['key_version'] = $this->_get_key_version( $group );
40
+
41
+ $storage_key = $this->get_item_key( $key );
42
+ return wincache_ucache_set( $storage_key, serialize( $var ), $expire );
43
+ }
44
+
45
+ /**
46
+ * Returns data
47
+ *
48
+ * @param string $key
49
+ * @param string $group Used to differentiate between groups of cache values
50
+ * @return mixed
51
+ */
52
+ function get_with_old( $key, $group = '' ) {
53
+ $has_old_data = false;
54
+
55
+ $storage_key = $this->get_item_key( $key );
56
+
57
+ $v = @unserialize( wincache_ucache_get( $storage_key ) );
58
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
59
+ return array( null, $has_old_data );
60
+
61
+ $key_version = $this->_get_key_version( $group );
62
+ if ( $v['key_version'] == $key_version )
63
+ return array( $v, $has_old_data );
64
+
65
+ if ( $v['key_version'] > $key_version ) {
66
+ $this->_set_key_version( $v['key_version'], $group );
67
+ return array( $v, $has_old_data );
68
+ }
69
+
70
+ // key version is old
71
+ if ( !$this->_use_expired_data )
72
+ return array( null, $has_old_data );
73
+
74
+ // if we have expired data - update it for future use and let
75
+ // current process recalculate it
76
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
77
+ if ( $expires_at == null || time() > $expires_at ) {
78
+ $v['expires_at'] = time() + 30;
79
+ wincache_ucache_set( $storage_key, serialize( $v ), 0 );
80
+ $has_old_data = true;
81
+
82
+ return array( null, $has_old_data );
83
+ }
84
+
85
+ // return old version
86
+ return array( $v, $has_old_data );
87
+ }
88
+
89
+ /**
90
+ * Replaces data
91
+ *
92
+ * @param string $key
93
+ * @param mixed $var
94
+ * @param integer $expire
95
+ * @param string $group Used to differentiate between groups of cache values
96
+ * @return boolean
97
+ */
98
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
99
+ if ( $this->get( $key, $group ) !== false ) {
100
+ return $this->set( $key, $var, $expire, $group );
101
+ }
102
+
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * Deletes data
108
+ *
109
+ * @param string $key
110
+ * @param string $group
111
+ * @return boolean
112
+ */
113
+ function delete( $key, $group = '' ) {
114
+ $storage_key = $this->get_item_key( $key );
115
+
116
+ if ( $this->_use_expired_data ) {
117
+ $v = @unserialize( wincache_ucache_get( $storage_key ) );
118
+ if ( is_array( $v ) ) {
119
+ $v['key_version'] = 0;
120
+ wincache_ucache_set( $storage_key, serialize( $v ), 0 );
121
+ return true;
122
+ }
123
+ }
124
+
125
+ return wincache_ucache_delete( $storage_key );
126
+ }
127
+
128
+ /**
129
+ * Key to delete, deletes .old and primary if exists.
130
+ *
131
+ * @param unknown $key
132
+ * @return bool
133
+ */
134
+ function hard_delete( $key ) {
135
+ $storage_key = $this->get_item_key( $key );
136
+ return wincache_ucache_delete( $storage_key );
137
+ }
138
+
139
+ /**
140
+ * Flushes all data
141
+ *
142
+ * @param string $group Used to differentiate between groups of cache values
143
+ * @return boolean
144
+ */
145
+ function flush( $group = '' ) {
146
+ $this->_get_key_version( $group ); // initialize $this->_key_version
147
+ $this->_key_version[$group]++;
148
+ $this->_set_key_version( $this->_key_version[$group], $group );
149
+ return true;
150
+ }
151
+
152
+ /**
153
+ * Checks if engine can function properly in this environment
154
+ *
155
+ * @return bool
156
+ */
157
+ public function available() {
158
+ return function_exists( 'wincache_ucache_set' );
159
+ }
160
+
161
+ /**
162
+ * Returns key postfix
163
+ *
164
+ * @param string $group Used to differentiate between groups of cache values
165
+ * @return integer
166
+ */
167
+ private function _get_key_version( $group = '' ) {
168
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
169
+ $v = wincache_ucache_get( $this->_get_key_version_key( $group ) );
170
+ $v = intval( $v );
171
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
172
+ }
173
+
174
+ return $this->_key_version[$group];
175
+ }
176
+
177
+ /**
178
+ * Sets new key version
179
+ *
180
+ * @param unknown $v
181
+ * @param string $group Used to differentiate between groups of cache values
182
+ * @return boolean
183
+ */
184
+ private function _set_key_version( $v, $group ) {
185
+ wincache_ucache_set( $this->_get_key_version_key( $group ), $v, 0 );
186
+ }
187
+
188
+ /**
189
+ * Used to replace as atomically as possible known value to new one
190
+ */
191
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
192
+ // cant guarantee atomic action here, filelocks fail often
193
+ $value = $this->get( $key );
194
+ if ( isset( $old_value['content'] ) &&
195
+ $value['content'] != $old_value['content'] )
196
+ return false;
197
+
198
+ return $this->set( $key, $new_value );
199
+ }
200
+
201
+ /**
202
+ * Use key as a counter and add integet value to it
203
+ */
204
+ public function counter_add( $key, $value ) {
205
+ if ( $value == 0 )
206
+ return true;
207
+
208
+ $storage_key = $this->get_item_key( $key );
209
+ $r = wincache_ucache_inc( $storage_key, $value );
210
+ if ( !$r ) // it doesnt initialize counter by itself
211
+ $this->counter_set( $key, 0 );
212
+
213
+ return $r;
214
+ }
215
+
216
+ /**
217
+ * Use key as a counter and add integet value to it
218
+ */
219
+ public function counter_set( $key, $value ) {
220
+ $storage_key = $this->get_item_key( $key );
221
+ return wincache_ucache_set( $storage_key, $value );
222
+ }
223
+
224
+ /**
225
+ * Get counter's value
226
+ */
227
+ public function counter_get( $key ) {
228
+ $storage_key = $this->get_item_key( $key );
229
+ $v = (int)wincache_ucache_get( $storage_key );
230
+
231
+ return $v;
232
+ }
233
+ }
Cache_Xcache.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * XCache class
6
+ */
7
+ class Cache_Xcache extends Cache_Base {
8
+
9
+ /*
10
+ * Used for faster flushing
11
+ *
12
+ * @var integer $_key_version
13
+ */
14
+ private $_key_version = array();
15
+
16
+ /**
17
+ * Adds data
18
+ *
19
+ * @param string $key
20
+ * @param mixed $var
21
+ * @param integer $expire
22
+ * @param string $group Used to differentiate between groups of cache values
23
+ * @return boolean
24
+ */
25
+ function add( $key, &$var, $expire = 0, $group = '' ) {
26
+ if ( $this->get( $key, $group ) === false ) {
27
+ return $this->set( $key, $var, $expire, $group );
28
+ }
29
+
30
+ return false;
31
+ }
32
+
33
+ /**
34
+ * Sets data
35
+ *
36
+ * @param string $key
37
+ * @param mixed $var
38
+ * @param integer $expire
39
+ * @param string $group Used to differentiate between groups of cache values
40
+ * @return boolean
41
+ */
42
+ function set( $key, $var, $expire = 0, $group = '' ) {
43
+ $var['key_version'] = $this->_get_key_version( $group );
44
+
45
+ $storage_key = $this->get_item_key( $key );
46
+ return xcache_set( $storage_key, serialize( $var ), $expire );
47
+ }
48
+
49
+ /**
50
+ * Returns data
51
+ *
52
+ * @param string $key
53
+ * @param string $group Used to differentiate between groups of cache values
54
+ * @return mixed
55
+ */
56
+ function get_with_old( $key, $group = '' ) {
57
+ $has_old_data = false;
58
+
59
+ $storage_key = $this->get_item_key( $key );
60
+
61
+ $v = @unserialize( xcache_get( $storage_key ) );
62
+ if ( !is_array( $v ) || !isset( $v['key_version'] ) )
63
+ return array( null, $has_old_data );
64
+
65
+ $key_version = $this->_get_key_version( $group );
66
+ if ( $v['key_version'] == $key_version )
67
+ return array( $v, $has_old_data );
68
+
69
+ if ( $v['key_version'] > $key_version ) {
70
+ $this->_set_key_version( $v['key_version'], $group );
71
+ return array( $v, $has_old_data );
72
+ }
73
+
74
+ // key version is old
75
+ if ( !$this->_use_expired_data )
76
+ return array( null, $has_old_data );
77
+
78
+ // if we have expired data - update it for future use and let
79
+ // current process recalculate it
80
+ $expires_at = isset( $v['expires_at'] ) ? $v['expires_at'] : null;
81
+ if ( $expires_at == null || time() > $expires_at ) {
82
+ $v['expires_at'] = time() + 30;
83
+ xcache_set( $storage_key, serialize( $v ), 0 );
84
+ $has_old_data = true;
85
+
86
+ return array( null, $has_old_data );
87
+ }
88
+
89
+ // return old version
90
+ return array( $v, $has_old_data );
91
+ }
92
+
93
+ /**
94
+ * Replaces data
95
+ *
96
+ * @param string $key
97
+ * @param mixed $var
98
+ * @param integer $expire
99
+ * @param string $group Used to differentiate between groups of cache values
100
+ * @return boolean
101
+ */
102
+ function replace( $key, &$var, $expire = 0, $group = '' ) {
103
+ if ( $this->get( $key, $group ) !== false ) {
104
+ return $this->set( $key, $var, $expire, $group );
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Deletes data
112
+ *
113
+ * @param string $key
114
+ * @param string $group
115
+ * @return boolean
116
+ */
117
+ function delete( $key, $group = '' ) {
118
+ $storage_key = $this->get_item_key( $key );
119
+
120
+ if ( $this->_use_expired_data ) {
121
+ $v = @unserialize( xcache_get( $storage_key ) );
122
+ if ( is_array( $v ) ) {
123
+ $v['key_version'] = 0;
124
+ xcache_set( $storage_key, serialize( $v ), 0 );
125
+ return true;
126
+ }
127
+ }
128
+
129
+ return xcache_unset( $storage_key );
130
+ }
131
+
132
+ /**
133
+ * Key to delete, deletes .old and primary if exists.
134
+ *
135
+ * @param unknown $key
136
+ * @return bool
137
+ */
138
+ function hard_delete( $key ) {
139
+ $storage_key = $this->get_item_key( $key );
140
+ return xcache_unset( $storage_key );
141
+ }
142
+
143
+ /**
144
+ * Flushes all data
145
+ *
146
+ * @param string $group Used to differentiate between groups of cache values
147
+ * @return boolean
148
+ */
149
+ function flush( $group = '' ) {
150
+ $this->_get_key_version( $group ); // initialize $this->_key_version
151
+ $this->_key_version[$group]++;
152
+ $this->_set_key_version( $this->_key_version[$group], $group );
153
+ return true;
154
+ }
155
+
156
+ /**
157
+ * Checks if engine can function properly in this environment
158
+ *
159
+ * @return bool
160
+ */
161
+ public function available() {
162
+ return function_exists( 'xcache_set' );
163
+ }
164
+
165
+ /**
166
+ * Returns key postfix
167
+ *
168
+ * @param string $group Used to differentiate between groups of cache values
169
+ * @return integer
170
+ */
171
+ private function _get_key_version( $group = '' ) {
172
+ if ( !isset( $this->_key_version[$group] ) || $this->_key_version[$group] <= 0 ) {
173
+ $v = xcache_get( $this->_get_key_version_key( $group ) );
174
+ $v = intval( $v );
175
+ $this->_key_version[$group] = ( $v > 0 ? $v : 1 );
176
+ }
177
+
178
+ return $this->_key_version[$group];
179
+ }
180
+
181
+ /**
182
+ * Sets new key version
183
+ *
184
+ * @param unknown $v
185
+ * @param string $group Used to differentiate between groups of cache values
186
+ * @return boolean
187
+ */
188
+ private function _set_key_version( $v, $group = '' ) {
189
+ xcache_set( $this->_get_key_version_key( $group ), $v, 0 );
190
+ }
191
+
192
+ /**
193
+ * Used to replace as atomically as possible known value to new one
194
+ */
195
+ public function set_if_maybe_equals( $key, $old_value, $new_value ) {
196
+ // cant guarantee atomic action here, filelocks fail often
197
+ $value = $this->get( $key );
198
+ if ( isset( $old_value['content'] ) &&
199
+ $value['content'] != $old_value['content'] )
200
+ return false;
201
+
202
+ return $this->set( $key, $new_value );
203
+ }
204
+
205
+ /**
206
+ * Use key as a counter and add integet value to it
207
+ */
208
+ public function counter_add( $key, $value ) {
209
+ if ( $value == 0 )
210
+ return true;
211
+
212
+ $storage_key = $this->get_item_key( $key );
213
+ $r = xcache_inc( $storage_key, $value );
214
+ if ( !$r ) // it doesnt initialize counter by itself
215
+ $this->counter_set( $key, 0 );
216
+
217
+ return $r;
218
+ }
219
+
220
+ /**
221
+ * Use key as a counter and add integet value to it
222
+ */
223
+ public function counter_set( $key, $value ) {
224
+ $storage_key = $this->get_item_key( $key );
225
+ return xcache_set( $storage_key, $value );
226
+ }
227
+
228
+ /**
229
+ * Get counter's value
230
+ */
231
+ public function counter_get( $key ) {
232
+ $storage_key = $this->get_item_key( $key );
233
+ $v = (int)xcache_get( $storage_key );
234
+
235
+ return $v;
236
+ }
237
+ }
CdnEngine.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class CdnEngine
6
+ */
7
+ class CdnEngine {
8
+ /**
9
+ * Returns CdnEngine_Base instance
10
+ *
11
+ * @param string $engine
12
+ * @param array $config
13
+ * @return CdnEngine_Base
14
+ */
15
+ static function instance( $engine, $config = array() ) {
16
+ static $instances = array();
17
+
18
+ $instance_key = sprintf( '%s_%s', $engine, md5( serialize( $config ) ) );
19
+
20
+ if ( !isset( $instances[$instance_key] ) ) {
21
+ switch ( $engine ) {
22
+ case 'akamai':
23
+ $instances[$instance_key] = new CdnEngine_Mirror_Akamai( $config );
24
+ break;
25
+
26
+ case 'att':
27
+ $instances[$instance_key] = new CdnEngine_Mirror_Att( $config );
28
+ break;
29
+
30
+ case 'azure':
31
+ $instances[$instance_key] = new CdnEngine_Azure( $config );
32
+ break;
33
+
34
+ case 'cf':
35
+ $instances[$instance_key] = new CdnEngine_S3_Cf_S3( $config );
36
+ break;
37
+
38
+ case 'cf2':
39
+ $instances[$instance_key] = new CdnEngine_S3_Cf_Custom( $config );
40
+ break;
41
+
42
+ case 'cotendo':
43
+ $instances[$instance_key] = new CdnEngine_Mirror_Cotendo( $config );
44
+ break;
45
+
46
+ case 'edgecast':
47
+ $instances[$instance_key] = new CdnEngine_Mirror_Edgecast( $config );
48
+ break;
49
+
50
+ case 'ftp':
51
+ $instances[$instance_key] = new CdnEngine_Ftp( $config );
52
+ break;
53
+
54
+ case 'google_drive':
55
+ $instances[$instance_key] = new CdnEngine_GoogleDrive( $config );
56
+ break;
57
+
58
+ case 'highwinds':
59
+ $instances[$instance_key] = new CdnEngine_Mirror_Highwinds( $config );
60
+ break;
61
+
62
+ case 'maxcdn':
63
+ $instances[$instance_key] = new CdnEngine_Mirror_MaxCdn( $config );
64
+ break;
65
+
66
+ case 'mirror':
67
+ $instances[$instance_key] = new CdnEngine_Mirror( $config );
68
+ break;
69
+
70
+ case 'netdna':
71
+ $instances[$instance_key] = new CdnEngine_Mirror_Netdna( $config );
72
+ break;
73
+
74
+ case 'rackspace_cdn':
75
+ $instances[$instance_key] = new CdnEngine_Mirror_RackSpaceCdn( $config );
76
+ break;
77
+
78
+ case 'rscf':
79
+ $instances[$instance_key] =
80
+ new CdnEngine_RackSpaceCloudFiles( $config );
81
+ break;
82
+
83
+ case 's3':
84
+ $instances[$instance_key] = new CdnEngine_S3( $config );
85
+ break;
86
+
87
+ case 's3_compatible':
88
+ $instances[$instance_key] = new CdnEngine_S3_Compatible( $config );
89
+ break;
90
+
91
+ default :
92
+ trigger_error( 'Incorrect CDN engine', E_USER_WARNING );
93
+ $instances[$instance_key] = new CdnEngine_Base();
94
+ break;
95
+ }
96
+ }
97
+
98
+ return $instances[$instance_key];
99
+ }
100
+ }
CdnEngine_Azure.php ADDED
@@ -0,0 +1,488 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Windows Azure Storage CDN engine
6
+ */
7
+ class CdnEngine_Azure extends CdnEngine_Base {
8
+ /**
9
+ * Storage client object
10
+ *
11
+ * @var Microsoft_WindowsAzure_Storage_Blob
12
+ */
13
+ var $_client = null;
14
+
15
+ /**
16
+ * PHP5 Constructor
17
+ *
18
+ * @param array $config
19
+ */
20
+ function __construct( $config = array() ) {
21
+ $config = array_merge( array(
22
+ 'user' => '',
23
+ 'key' => '',
24
+ 'container' => '',
25
+ 'cname' => array(),
26
+ ), $config );
27
+
28
+ parent::__construct( $config );
29
+ }
30
+
31
+ /**
32
+ * Inits storage client object
33
+ *
34
+ * @param string $error
35
+ * @return boolean
36
+ */
37
+ function _init( &$error ) {
38
+ if ( empty( $this->_config['user'] ) ) {
39
+ $error = 'Empty account name.';
40
+
41
+ return false;
42
+ }
43
+
44
+ if ( empty( $this->_config['key'] ) ) {
45
+ $error = 'Empty account key.';
46
+
47
+ return false;
48
+ }
49
+
50
+ if ( empty( $this->_config['container'] ) ) {
51
+ $error = 'Empty container name.';
52
+
53
+ return false;
54
+ }
55
+
56
+ set_include_path( get_include_path() . PATH_SEPARATOR . W3TC_LIB_DIR );
57
+
58
+ require_once 'Microsoft/WindowsAzure/Storage/Blob.php';
59
+
60
+ $this->_client = new \Microsoft_WindowsAzure_Storage_Blob(
61
+ \Microsoft_WindowsAzure_Storage::URL_CLOUD_BLOB,
62
+ $this->_config['user'],
63
+ $this->_config['key'],
64
+ false,
65
+ \Microsoft_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry()
66
+ );
67
+
68
+ return true;
69
+ }
70
+
71
+ /**
72
+ * Uploads files to S3
73
+ *
74
+ * @param array $files
75
+ * @param array $results
76
+ * @param boolean $force_rewrite
77
+ * @return boolean
78
+ */
79
+ function upload( $files, &$results, $force_rewrite = false,
80
+ $timeout_time = NULL ) {
81
+ $error = null;
82
+
83
+ if ( !$this->_init( $error ) ) {
84
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
85
+
86
+ return false;
87
+ }
88
+
89
+ foreach ( $files as $file ) {
90
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
91
+ break;
92
+
93
+ $remote_path = $file['remote_path'];
94
+
95
+ $results[] = $this->_upload( $file, $force_rewrite );
96
+
97
+ if ( $this->_config['compression'] && $this->_may_gzip( $remote_path ) ) {
98
+ $file['remote_path_gzip'] = $remote_path . $this->_gzip_extension;
99
+
100
+ $results[] = $this->_upload_gzip( $file, $force_rewrite );
101
+ }
102
+ }
103
+
104
+ return !$this->_is_error( $results );
105
+ }
106
+
107
+ /**
108
+ * Uploads file
109
+ *
110
+ * @param string $local_path
111
+ * @param string $remote_path
112
+ * @param bool $force_rewrite
113
+ * @return array
114
+ */
115
+ function _upload( $file, $force_rewrite = false ) {
116
+ $local_path = $file['local_path'];
117
+ $remote_path = $file['remote_path'];
118
+
119
+ if ( !file_exists( $local_path ) ) {
120
+ return $this->_get_result( $local_path, $remote_path,
121
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
122
+ }
123
+
124
+ $md5 = @md5_file( $local_path );
125
+ $content_md5 = $this->_get_content_md5( $md5 );
126
+
127
+ if ( !$force_rewrite ) {
128
+ try {
129
+ $properties = $this->_client->getBlobProperties( $this->_config['container'], $remote_path );
130
+ $size = @filesize( $local_path );
131
+
132
+ if ( $size === (int) $properties->Size && $content_md5 === $properties->ContentMd5 ) {
133
+ return $this->_get_result( $local_path, $remote_path,
134
+ W3TC_CDN_RESULT_OK, 'File up-to-date.', $file );
135
+ }
136
+ } catch ( \Exception $exception ) {
137
+ }
138
+ }
139
+
140
+ $headers = $this->_get_headers( $file );
141
+ $headers = array_merge( $headers, array(
142
+ 'Content-MD5' => $content_md5
143
+ ) );
144
+
145
+ try {
146
+ $this->_client->putBlob( $this->_config['container'], $remote_path, $local_path, array(), null, $headers );
147
+ } catch ( \Exception $exception ) {
148
+ return $this->_get_result( $local_path, $remote_path,
149
+ W3TC_CDN_RESULT_ERROR,
150
+ sprintf( 'Unable to put blob (%s).', $exception->getMessage() ),
151
+ $file );
152
+ }
153
+
154
+ return $this->_get_result( $local_path, $remote_path, W3TC_CDN_RESULT_OK,
155
+ 'OK', $file );
156
+ }
157
+
158
+ /**
159
+ * Uploads gzipped file
160
+ *
161
+ * @param array $file CDN file array
162
+ * @param bool $force_rewrite
163
+ * @return array
164
+ */
165
+ function _upload_gzip( $file, $force_rewrite = false ) {
166
+ $local_path = $file['local_path'];
167
+ $remote_path = $file['remote_path_gzip'];
168
+
169
+ if ( !function_exists( 'gzencode' ) ) {
170
+ return $this->_get_result( $local_path, $remote_path,
171
+ W3TC_CDN_RESULT_ERROR, "GZIP library doesn't exists.", $file );
172
+ }
173
+
174
+ if ( !file_exists( $local_path ) ) {
175
+ return $this->_get_result( $local_path, $remote_path,
176
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
177
+ }
178
+
179
+ $contents = @file_get_contents( $local_path );
180
+
181
+ if ( $contents === false ) {
182
+ return $this->_get_result( $local_path, $remote_path,
183
+ W3TC_CDN_RESULT_ERROR, 'Unable to read file.', $file );
184
+ }
185
+
186
+ $data = gzencode( $contents );
187
+ $md5 = md5( $data );
188
+ $content_md5 = $this->_get_content_md5( $md5 );
189
+
190
+ if ( !$force_rewrite ) {
191
+ try {
192
+ $properties = $this->_client->getBlobProperties( $this->_config['container'], $remote_path );
193
+ $size = @filesize( $local_path );
194
+
195
+ if ( $size === (int) $properties->Size && $content_md5 === $properties->ContentMd5 ) {
196
+ return $this->_get_result( $local_path, $remote_path,
197
+ W3TC_CDN_RESULT_OK, 'File up-to-date.', $file );
198
+ }
199
+ } catch ( \Exception $exception ) {
200
+ }
201
+ }
202
+
203
+ $headers = $this->_get_headers( $file );
204
+ $headers = array_merge( $headers, array(
205
+ 'Content-MD5' => $content_md5,
206
+ 'Content-Encoding' => 'gzip'
207
+ ) );
208
+
209
+ try {
210
+ $this->_client->putBlobData( $this->_config['container'], $remote_path, $data, array(), null, $headers );
211
+ } catch ( \Exception $exception ) {
212
+ return $this->_get_result( $local_path, $remote_path,
213
+ W3TC_CDN_RESULT_ERROR,
214
+ sprintf( 'Unable to put blob (%s).', $exception->getMessage() ),
215
+ $file );
216
+ }
217
+
218
+ return $this->_get_result( $local_path, $remote_path,
219
+ W3TC_CDN_RESULT_OK, 'OK', $file );
220
+ }
221
+
222
+ /**
223
+ * Deletes files from storage
224
+ *
225
+ * @param array $files
226
+ * @param array $results
227
+ * @return boolean
228
+ */
229
+ function delete( $files, &$results ) {
230
+ $error = null;
231
+
232
+ if ( !$this->_init( $error ) ) {
233
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
234
+
235
+ return false;
236
+ }
237
+
238
+ foreach ( $files as $file ) {
239
+ $local_path = $file['local_path'];
240
+ $remote_path = $file['remote_path'];
241
+
242
+ try {
243
+ $this->_client->deleteBlob( $this->_config['container'], $remote_path );
244
+ $results[] = $this->_get_result( $local_path, $remote_path,
245
+ W3TC_CDN_RESULT_OK, 'OK', $file );
246
+ } catch ( \Exception $exception ) {
247
+ $results[] = $this->_get_result( $local_path, $remote_path,
248
+ W3TC_CDN_RESULT_ERROR,
249
+ sprintf( 'Unable to delete blob (%s).', $exception->getMessage() ),
250
+ $file );
251
+ }
252
+
253
+ if ( $this->_config['compression'] ) {
254
+ $remote_path_gzip = $remote_path . $this->_gzip_extension;
255
+
256
+ try {
257
+ $this->_client->deleteBlob( $this->_config['container'], $remote_path_gzip );
258
+ $results[] = $this->_get_result( $local_path,
259
+ $remote_path_gzip, W3TC_CDN_RESULT_OK, 'OK',
260
+ $file );
261
+ } catch ( \Exception $exception ) {
262
+ $results[] = $this->_get_result( $local_path,
263
+ $remote_path_gzip, W3TC_CDN_RESULT_ERROR,
264
+ sprintf( 'Unable to delete blob (%s).',
265
+ $exception->getMessage() ),
266
+ $file );
267
+ }
268
+ }
269
+ }
270
+
271
+ return !$this->_is_error( $results );
272
+ }
273
+
274
+ /**
275
+ * Tests S3
276
+ *
277
+ * @param string $error
278
+ * @return boolean
279
+ */
280
+ function test( &$error ) {
281
+ if ( !parent::test( $error ) ) {
282
+ return false;
283
+ }
284
+
285
+ $string = 'test_azure_' . md5( time() );
286
+
287
+ if ( !$this->_init( $error ) ) {
288
+ return false;
289
+ }
290
+
291
+ try {
292
+ $containers = $this->_client->listContainers();
293
+ } catch ( \Exception $exception ) {
294
+ $error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() );
295
+
296
+ return false;
297
+ }
298
+
299
+ $container = null;
300
+
301
+ foreach ( (array) $containers as $_container ) {
302
+ if ( $_container->Name == $this->_config['container'] ) {
303
+ $container = $_container;
304
+ break;
305
+ }
306
+ }
307
+
308
+ if ( !$container ) {
309
+ $error = sprintf( 'Container doesn\'t exist: %s.', $this->_config['container'] );
310
+
311
+ return false;
312
+ }
313
+
314
+ try {
315
+ $this->_client->putBlobData( $this->_config['container'], $string, $string );
316
+ } catch ( \Exception $exception ) {
317
+ $error = sprintf( 'Unable to put blob data (%s).', $exception->getMessage() );
318
+
319
+ return false;
320
+ }
321
+
322
+ try {
323
+ $data = $this->_client->getBlobData( $this->_config['container'], $string );
324
+ } catch ( \Exception $exception ) {
325
+ $error = sprintf( 'Unable to get blob data (%s).', $exception->getMessage() );
326
+
327
+ return false;
328
+ }
329
+
330
+ if ( $data != $string ) {
331
+ try {
332
+ $this->_client->deleteBlob( $this->_config['container'], $string );
333
+ } catch ( \Exception $exception ) {
334
+ }
335
+
336
+ $error = 'Blob datas are not equal.';
337
+
338
+ return false;
339
+ }
340
+
341
+ try {
342
+ $this->_client->deleteBlob( $this->_config['container'], $string );
343
+ } catch ( \Exception $exception ) {
344
+ $error = sprintf( 'Unable to delete blob (%s).', $exception->getMessage() );
345
+
346
+ return false;
347
+ }
348
+
349
+ return true;
350
+ }
351
+
352
+ /**
353
+ * Returns CDN domain
354
+ *
355
+ * @return array
356
+ */
357
+ function get_domains() {
358
+ if ( !empty( $this->_config['cname'] ) ) {
359
+ return (array) $this->_config['cname'];
360
+ } elseif ( !empty( $this->_config['user'] ) ) {
361
+ $domain = sprintf( '%s.blob.core.windows.net', $this->_config['user'] );
362
+
363
+ return array(
364
+ $domain
365
+ );
366
+ }
367
+
368
+ return array();
369
+ }
370
+
371
+ /**
372
+ * Returns via string
373
+ *
374
+ * @return string
375
+ */
376
+ function get_via() {
377
+ return sprintf( 'Windows Azure Storage: %s', parent::get_via() );
378
+ }
379
+
380
+ /**
381
+ * Creates bucket
382
+ *
383
+ * @param string $container_id
384
+ * @param string $error
385
+ * @return boolean
386
+ */
387
+ function create_container( &$container_id, &$error ) {
388
+ if ( !$this->_init( $error ) ) {
389
+ return false;
390
+ }
391
+
392
+ try {
393
+ $containers = $this->_client->listContainers();
394
+ } catch ( \Exception $exception ) {
395
+ $error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() );
396
+
397
+ return false;
398
+ }
399
+
400
+ if ( in_array( $this->_config['container'], (array) $containers ) ) {
401
+ $error = sprintf( 'Container already exists: %s.', $this->_config['container'] );
402
+
403
+ return false;
404
+ }
405
+
406
+ try {
407
+ $this->_client->createContainer( $this->_config['container'] );
408
+ $this->_client->setContainerAcl( $this->_config['container'], \Microsoft_WindowsAzure_Storage_Blob::ACL_PUBLIC_BLOB );
409
+ } catch ( \Exception $exception ) {
410
+ $error = sprintf( 'Unable to create container: %s (%s)', $this->_config['container'], $exception->getMessage() );
411
+
412
+ return false;
413
+ }
414
+
415
+ return true;
416
+ }
417
+
418
+ /**
419
+ * Returns Content-MD5 header value
420
+ *
421
+ * @param string $string
422
+ * @return string
423
+ */
424
+ function _get_content_md5( $md5 ) {
425
+ return base64_encode( pack( 'H*', $md5 ) );
426
+ }
427
+
428
+ /**
429
+ * Formats object URL
430
+ *
431
+ * @param string $path
432
+ * @return string
433
+ */
434
+ function _format_url( $path ) {
435
+ $domain = $this->get_domain( $path );
436
+
437
+ if ( $domain && !empty( $this->_config['container'] ) ) {
438
+ $scheme = $this->_get_scheme();
439
+ $url = sprintf( '%s://%s/%s/%s', $scheme, $domain, $this->_config['container'], $path );
440
+
441
+ return $url;
442
+ }
443
+
444
+ return false;
445
+ }
446
+
447
+ /**
448
+ * Returns array of headers
449
+ *
450
+ * @param array $file CDN file array
451
+ * @return array
452
+ */
453
+ function _get_headers( $file ) {
454
+ $allowed_headers = array(
455
+ 'Content-Length',
456
+ 'Content-Type',
457
+ 'Content-Encoding',
458
+ 'Content-Language',
459
+ 'Content-MD5',
460
+ 'Cache-Control',
461
+ );
462
+
463
+ $headers = parent::_get_headers( $file, true );
464
+
465
+ foreach ( $headers as $header => $value ) {
466
+ if ( !in_array( $header, $allowed_headers ) ) {
467
+ unset( $headers[$header] );
468
+ }
469
+ }
470
+
471
+ return $headers;
472
+ }
473
+
474
+ /**
475
+ * How and if headers should be set
476
+ *
477
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
478
+ */
479
+ function headers_support() {
480
+ return W3TC_CDN_HEADER_UPLOADABLE;
481
+ }
482
+
483
+ function get_prepend_path( $path ) {
484
+ $path = parent::get_prepend_path( $path );
485
+ $path = $this->_config['container'] ? trim( $path, '/' ) . '/' . trim( $this->_config['container'], '/' ): $path;
486
+ return $path;
487
+ }
488
+ }
CdnEngine_Base.php ADDED
@@ -0,0 +1,705 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Base class
6
+ */
7
+ define( 'W3TC_CDN_RESULT_HALT', -1 );
8
+ define( 'W3TC_CDN_RESULT_ERROR', 0 );
9
+ define( 'W3TC_CDN_RESULT_OK', 1 );
10
+ define( 'W3TC_CDN_HEADER_NONE', 'none' );
11
+ define( 'W3TC_CDN_HEADER_UPLOADABLE', 'uploadable' );
12
+ define( 'W3TC_CDN_HEADER_MIRRORING', 'mirroring' );
13
+
14
+ /**
15
+ * class CdnEngine_Base
16
+ */
17
+ class CdnEngine_Base {
18
+ /**
19
+ * Engine configuration
20
+ *
21
+ * @var array
22
+ */
23
+ var $_config = array();
24
+
25
+ /**
26
+ * Cache config
27
+ *
28
+ * @var array
29
+ */
30
+ var $cache_config = array();
31
+
32
+ /**
33
+ * gzip extension
34
+ *
35
+ * @var string
36
+ */
37
+ var $_gzip_extension = '.gzip';
38
+
39
+ /**
40
+ * Last error
41
+ *
42
+ * @var string
43
+ */
44
+ var $_last_error = '';
45
+
46
+ /**
47
+ * PHP5 Constructor
48
+ *
49
+ * @param array $config
50
+ */
51
+ function __construct( $config = array() ) {
52
+ $this->_config = array_merge( array(
53
+ 'debug' => false,
54
+ 'ssl' => 'auto',
55
+ 'compression' => false
56
+ ), $config );
57
+ }
58
+
59
+ /**
60
+ * Upload files to CDN
61
+ *
62
+ * @param array $files takes array consisting of array(array('local_path'=>'', 'remote_path'=>''))
63
+ * @param array $results
64
+ * @param boolean $force_rewrite
65
+ * @return boolean
66
+ */
67
+ function upload( $files, &$results, $force_rewrite = false,
68
+ $timeout_time = NULL ) {
69
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT,
70
+ 'Not implemented.' );
71
+
72
+ return false;
73
+ }
74
+
75
+ /**
76
+ * Delete files from CDN
77
+ *
78
+ * @param array $files
79
+ * @param array $results
80
+ * @return boolean
81
+ */
82
+ function delete( $files, &$results ) {
83
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT,
84
+ 'Not implemented.' );
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Purge files from CDN
91
+ *
92
+ * @param array $files
93
+ * @param array $results
94
+ * @return boolean
95
+ */
96
+ function purge( $files, &$results ) {
97
+ return $this->upload( $files, $results, true );
98
+ }
99
+
100
+ /**
101
+ * Purge CDN completely
102
+ *
103
+ * @param unknown $results
104
+ * @return bool
105
+ */
106
+ function purge_all( &$results ) {
107
+ $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT,
108
+ 'Not implemented.' );
109
+
110
+ return false;
111
+ }
112
+
113
+ /**
114
+ * Test CDN server
115
+ *
116
+ * @param string $error
117
+ * @return boolean
118
+ */
119
+ function test( &$error ) {
120
+ if ( !$this->_test_domains( $error ) ) {
121
+ return false;
122
+ }
123
+
124
+ return true;
125
+ }
126
+
127
+ /**
128
+ * Create bucket / container for some CDN engines
129
+ *
130
+ * @param string $container_id
131
+ * @param string $error
132
+ * @return boolean
133
+ */
134
+ function create_container( &$container_id, &$error ) {
135
+ $error = 'Not implemented.';
136
+
137
+ return false;
138
+ }
139
+
140
+ /**
141
+ * Returns first domain
142
+ *
143
+ * @param string $path
144
+ * @return string
145
+ */
146
+ function get_domain( $path = '' ) {
147
+ $domains = $this->get_domains();
148
+ $count = count( $domains );
149
+
150
+ if ( $count ) {
151
+ switch ( true ) {
152
+ /**
153
+ * Reserved CSS
154
+ */
155
+ case ( isset( $domains[0] ) && $this->_is_css( $path ) ):
156
+ $domain = $domains[0];
157
+ break;
158
+
159
+
160
+ /**
161
+ * Reserved JS after body
162
+ */
163
+ case ( isset( $domains[2] ) && $this->_is_js_body( $path ) ):
164
+ $domain = $domains[2];
165
+ break;
166
+
167
+ /**
168
+ * Reserved JS before /body
169
+ */
170
+ case ( isset( $domains[3] ) && $this->_is_js_footer( $path ) ):
171
+ $domain = $domains[3];
172
+ break;
173
+
174
+ /**
175
+ * Reserved JS in head, moved here due to greedy regex
176
+ */
177
+ case ( isset( $domains[1] ) && $this->_is_js( $path ) ):
178
+ $domain = $domains[1];
179
+ break;
180
+
181
+ default:
182
+ if ( $count > 4 ) {
183
+ $domain = $this->_get_domain( array_slice( $domains, 4 ),
184
+ $path );
185
+ } else {
186
+ $domain = $this->_get_domain( $domains, $path );
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Custom host for SSL
192
+ */
193
+ list( $domain_http, $domain_https ) = array_map( 'trim',
194
+ explode( ',', $domain . ',' ) );
195
+
196
+ $scheme = $this->_get_scheme();
197
+
198
+ switch ( $scheme ) {
199
+ case 'http':
200
+ $domain = $domain_http;
201
+ break;
202
+
203
+ case 'https':
204
+ $domain = ( $domain_https ? $domain_https : $domain_http );
205
+ break;
206
+ }
207
+
208
+ return $domain;
209
+ }
210
+
211
+ return false;
212
+ }
213
+
214
+ /**
215
+ * Returns array of CDN domains
216
+ *
217
+ * @return array
218
+ */
219
+ function get_domains() {
220
+ return array();
221
+ }
222
+
223
+ /**
224
+ * Returns via string
225
+ *
226
+ * @return string
227
+ */
228
+ function get_via() {
229
+ $domain = $this->get_domain();
230
+
231
+ if ( $domain ) {
232
+ return $domain;
233
+ }
234
+
235
+ return 'N/A';
236
+ }
237
+
238
+ /**
239
+ * Formats URL
240
+ *
241
+ * @param string $path
242
+ * @return string
243
+ */
244
+ function format_url( $path ) {
245
+ $url = $this->_format_url( $path );
246
+
247
+ if ( $url && $this->_config['compression'] &&
248
+ isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) &&
249
+ stristr( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false &&
250
+ $this->_may_gzip( $path ) ) {
251
+ if ( ( $qpos = strpos( $url, '?' ) ) !== false ) {
252
+ $url = substr_replace( $url, $this->_gzip_extension, $qpos, 0 );
253
+ } else {
254
+ $url .= $this->_gzip_extension;
255
+ }
256
+ }
257
+
258
+ return $url;
259
+ }
260
+
261
+ /**
262
+ * Returns prepend path
263
+ *
264
+ * @param string $path
265
+ * @return string
266
+ */
267
+ function get_prepend_path( $path ) {
268
+ $domain = $this->get_domain( $path );
269
+
270
+ if ( $domain ) {
271
+ $scheme = $this->_get_scheme();
272
+ $url = sprintf( '%s://%s', $scheme, $domain );
273
+
274
+ return $url;
275
+ }
276
+
277
+ return false;
278
+ }
279
+
280
+ /**
281
+ * Formats URL
282
+ *
283
+ * @param string $path
284
+ * @return string
285
+ */
286
+ function _format_url( $path ) {
287
+ $domain = $this->get_domain( $path );
288
+
289
+ if ( $domain ) {
290
+ $scheme = $this->_get_scheme();
291
+ $url = sprintf( '%s://%s/%s', $scheme, $domain, $path );
292
+
293
+ return $url;
294
+ }
295
+
296
+ return false;
297
+ }
298
+
299
+ /**
300
+ * Returns results
301
+ *
302
+ * @param array $files
303
+ * @param integer $result
304
+ * @param string $error
305
+ * @return array
306
+ */
307
+ function _get_results( $files, $result = W3TC_CDN_RESULT_OK,
308
+ $error = 'OK' ) {
309
+ $results = array();
310
+
311
+ foreach ( $files as $key => $file ) {
312
+ if ( is_array( $file ) ) {
313
+ $local_path = $file['local_path'];
314
+ $remote_path = $file['remote_path'];
315
+ } else {
316
+ $local_path = $key;
317
+ $remote_path = $file;
318
+ }
319
+
320
+ $results[] = $this->_get_result( $local_path, $remote_path, $result,
321
+ $error, $file );
322
+ }
323
+
324
+ return $results;
325
+ }
326
+
327
+ /**
328
+ * Returns file process result
329
+ *
330
+ * @param string $local_path
331
+ * @param string $remote_path
332
+ * @param integer $result
333
+ * @param string $error
334
+ * @return array
335
+ */
336
+ function _get_result( $local_path, $remote_path,
337
+ $result = W3TC_CDN_RESULT_OK, $error = 'OK', $descriptor = null ) {
338
+ if ( $this->_config['debug'] ) {
339
+ $this->_log( $local_path, $remote_path, $error );
340
+ }
341
+
342
+ return array(
343
+ 'local_path' => $local_path,
344
+ 'remote_path' => $remote_path,
345
+ 'result' => $result,
346
+ 'error' => $error,
347
+ 'descriptor' => $descriptor
348
+ );
349
+ }
350
+
351
+ /**
352
+ * Check for errors
353
+ *
354
+ * @param array $results
355
+ * @return bool
356
+ */
357
+ function _is_error( $results ) {
358
+ foreach ( $results as $result ) {
359
+ if ( $result['result'] != W3TC_CDN_RESULT_OK ) {
360
+ return true;
361
+ }
362
+ }
363
+
364
+ return false;
365
+ }
366
+
367
+ /**
368
+ * Returns headers for file
369
+ *
370
+ * @param array $file CDN file array
371
+ * @return array
372
+ */
373
+ function _get_headers( $file, $block_expires = false ) {
374
+
375
+ $local_path = $file['local_path'];
376
+ $mime_type = Util_Mime::get_mime_type( $local_path );
377
+ $last_modified = time();
378
+
379
+ $link = $file['original_url'];
380
+
381
+ $headers = array(
382
+ 'Content-Type' => $mime_type,
383
+ 'Last-Modified' => Util_Content::http_date( $last_modified ),
384
+ 'Access-Control-Allow-Origin' => '*',
385
+ 'Link' => '<' . $link .'>; rel="canonical"'
386
+ );
387
+
388
+ if ( isset( $this->cache_config[$mime_type] ) ) {
389
+ if ( $this->cache_config[$mime_type]['etag'] ) {
390
+ $headers['ETag'] = '"' . @md5_file( $local_path ) . '"';
391
+ }
392
+
393
+ if ( $this->cache_config[$mime_type]['w3tc'] ) {
394
+ $headers['X-Powered-By'] =
395
+ Util_Environment::w3tc_header( $this->_config );
396
+ }
397
+
398
+
399
+ $expires_set = false;
400
+ if ( !$block_expires &&
401
+ $this->cache_config[$mime_type]['expires'] ) {
402
+ $headers['Expires'] = Util_Content::http_date( time() +
403
+ $this->cache_config[$mime_type]['lifetime'] );
404
+ $expires_set = true;
405
+ }
406
+
407
+ switch ( $this->cache_config[$mime_type]['cache_control'] ) {
408
+ case 'cache':
409
+ $headers = array_merge( $headers, array(
410
+ 'Pragma' => 'public',
411
+ 'Cache-Control' => 'public'
412
+ ) );
413
+ break;
414
+
415
+ case 'cache_public_maxage':
416
+ $headers = array_merge( $headers, array(
417
+ 'Pragma' => 'public',
418
+ 'Cache-Control' => ( $expires_set ? '' : 'max-age=' .
419
+ $this->cache_config[$mime_type]['lifetime'] .', ' ) .
420
+ 'public'
421
+ ) );
422
+ break;
423
+
424
+ case 'cache_validation':
425
+ $headers = array_merge( $headers, array(
426
+ 'Pragma' => 'public',
427
+ 'Cache-Control' => 'public, must-revalidate, proxy-revalidate'
428
+ ) );
429
+ break;
430
+
431
+ case 'cache_noproxy':
432
+ $headers = array_merge( $headers, array(
433
+ 'Pragma' => 'public',
434
+ 'Cache-Control' => 'private, must-revalidate'
435
+ ) );
436
+ break;
437
+
438
+ case 'cache_maxage':
439
+ $headers = array_merge( $headers, array(
440
+ 'Pragma' => 'public',
441
+ 'Cache-Control' => ( $expires_set ? '' : 'max-age=' .
442
+ $this->cache_config[$mime_type]['lifetime'] .', ' ) .
443
+ 'public, must-revalidate, proxy-revalidate'
444
+ ) );
445
+ break;
446
+
447
+ case 'no_cache':
448
+ $headers = array_merge( $headers, array(
449
+ 'Pragma' => 'no-cache',
450
+ 'Cache-Control' => 'max-age=0, private, no-store, no-cache, must-revalidate'
451
+ ) );
452
+ break;
453
+ }
454
+ }
455
+
456
+ return $headers;
457
+ }
458
+
459
+ /**
460
+ * Use gzip compression only for text-based files
461
+ *
462
+ * @param string $file
463
+ * @return boolean
464
+ */
465
+ function _may_gzip( $file ) {
466
+ /**
467
+ * Remove query string
468
+ */
469
+ $file = preg_replace( '~\?.*$~', '', $file );
470
+
471
+ /**
472
+ * Check by file extension
473
+ */
474
+ if ( preg_match( '~\.(ico|js|css|xml|xsd|xsl|svg|htm|html|txt)$~i',
475
+ $file ) ) {
476
+ return true;
477
+ }
478
+
479
+ return false;
480
+ }
481
+
482
+ /**
483
+ * Test domains
484
+ *
485
+ * @param string $error
486
+ * @return boolean
487
+ */
488
+ function _test_domains( &$error ) {
489
+ $domains = $this->get_domains();
490
+
491
+ if ( !count( $domains ) ) {
492
+ $error = 'Empty hostname / CNAME list.';
493
+
494
+ return false;
495
+
496
+ }
497
+
498
+ foreach ( $domains as $domain ) {
499
+ $_domains = array_map( 'trim', explode( ',', $domain ) );
500
+
501
+ foreach ( $_domains as $_domain ) {
502
+ $matches = null;
503
+
504
+ if ( preg_match( '~^([a-z0-9\-\.]*)~i', $_domain, $matches ) ) {
505
+ $hostname = $matches[1];
506
+ } else {
507
+ $hostname = $_domain;
508
+ }
509
+
510
+ if ( !$hostname ) {
511
+ $error = 'Empty hostname';
512
+
513
+ return false;
514
+ }
515
+
516
+ if ( gethostbyname( $hostname ) === $hostname ) {
517
+ $error = sprintf( 'Unable to resolve hostname: %s.',
518
+ $hostname );
519
+
520
+ return false;
521
+ }
522
+ }
523
+ }
524
+
525
+ return true;
526
+ }
527
+
528
+ /**
529
+ * Check if css file
530
+ *
531
+ * @param string $path
532
+ * @return boolean
533
+ */
534
+ function _is_css( $path ) {
535
+ return preg_match( '~[a-zA-Z0-9\-_]*(\.include\.[0-9]+)?\.css$~',
536
+ $path );
537
+ }
538
+
539
+ /**
540
+ * Check if JS file in heeader
541
+ *
542
+ * @param string $path
543
+ * @return boolean
544
+ */
545
+ function _is_js( $path ) {
546
+ return preg_match( '~([a-z0-9\-_]+(\.include\.[a-z0-9]+)\.js)$~',
547
+ $path ) ||
548
+ preg_match( '~[\w\d\-_]+\.js~', $path );
549
+ }
550
+
551
+ /**
552
+ * Check if JS file after body
553
+ *
554
+ * @param string $path
555
+ * @return boolean
556
+ */
557
+ function _is_js_body( $path ) {
558
+ return preg_match( '~[a-z0-9\-_]+(\.include-body\.[a-z0-9]+)\.js$~',
559
+ $path );
560
+ }
561
+
562
+ /**
563
+ * Check if JS file before /body
564
+ *
565
+ * @param string $path
566
+ * @return boolean
567
+ */
568
+ function _is_js_footer( $path ) {
569
+ return preg_match( '~[a-z0-9\-_]+(\.include-footer\.[a-z0-9]+)\.js$~',
570
+ $path );
571
+ }
572
+
573
+ /**
574
+ * Returns domain for path
575
+ *
576
+ * @param array $domains
577
+ * @param string $path
578
+ * @return string
579
+ */
580
+ function _get_domain( $domains, $path ) {
581
+ $count = count( $domains );
582
+
583
+ if ( $count ) {
584
+ /**
585
+ * Use for equal URLs same host to allow caching by browser
586
+ */
587
+ $hash = $this->_get_hash( $path );
588
+ $domain = $domains[$hash % $count];
589
+
590
+ return $domain;
591
+ }
592
+
593
+ return false;
594
+ }
595
+
596
+ /**
597
+ * Returns integer hash for key
598
+ *
599
+ * @param string $key
600
+ * @return integer
601
+ */
602
+ function _get_hash( $key ) {
603
+ $hash = abs( crc32( $key ) );
604
+
605
+ return $hash;
606
+ }
607
+
608
+ /**
609
+ * Returns scheme
610
+ *
611
+ * @return string
612
+ */
613
+ function _get_scheme() {
614
+ switch ( $this->_config['ssl'] ) {
615
+ default:
616
+ case 'auto':
617
+ $scheme = ( Util_Environment::is_https() ? 'https' : 'http' );
618
+ break;
619
+
620
+ case 'enabled':
621
+ $scheme = 'https';
622
+ break;
623
+
624
+ case 'disabled':
625
+ $scheme = 'http';
626
+ break;
627
+ case 'rejected':
628
+ $scheme = 'http';
629
+ break;
630
+ }
631
+
632
+ return $scheme;
633
+ }
634
+
635
+ /**
636
+ * Write log entry
637
+ *
638
+ * @param string $local_path
639
+ * @param string $remote_path
640
+ * @param string $error
641
+ * @return bool|int
642
+ */
643
+ function _log( $local_path, $remote_path, $error ) {
644
+ $data = sprintf( "[%s] [%s => %s] %s\n", date( 'r' ), $local_path,
645
+ $remote_path, $error );
646
+ $data = strtr( $data, '<>', '..' );
647
+
648
+ $filename = Util_Debug::log_filename( 'cdn' );
649
+
650
+ return @file_put_contents( $filename, $data, FILE_APPEND );
651
+ }
652
+
653
+ /**
654
+ * Our error handler
655
+ *
656
+ * @param integer $errno
657
+ * @param string $errstr
658
+ * @return boolean
659
+ */
660
+ function _error_handler( $errno, $errstr ) {
661
+ $this->_last_error = $errstr;
662
+
663
+ return false;
664
+ }
665
+
666
+ /**
667
+ * Returns last error
668
+ *
669
+ * @return string
670
+ */
671
+ function _get_last_error() {
672
+ return $this->_last_error;
673
+ }
674
+
675
+ /**
676
+ * Set our error handler
677
+ *
678
+ * @return void
679
+ */
680
+ function _set_error_handler() {
681
+ set_error_handler( array(
682
+ $this,
683
+ '_error_handler'
684
+ ) );
685
+ }
686
+
687
+ /**
688
+ * Restore prev error handler
689
+ *
690
+ * @return void
691
+ */
692
+ function _restore_error_handler() {
693
+ restore_error_handler();
694
+ }
695
+
696
+ /**
697
+ * How and if headers should be set
698
+ *
699
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE,
700
+ * W3TC_CDN_HEADER_MIRRORING
701
+ */
702
+ function headers_support() {
703
+ return W3TC_CDN_HEADER_NONE;
704
+ }
705
+ }
CdnEngine_Ftp.php ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN FTP Class
6
+ */
7
+
8
+ define( 'W3TC_CDN_FTP_CONNECT_TIMEOUT', 30 );
9
+
10
+ /**
11
+ * class CdnEngine_Ftp
12
+ */
13
+ class CdnEngine_Ftp extends CdnEngine_Base {
14
+ /**
15
+ * FTP resource
16
+ *
17
+ * @var resource
18
+ */
19
+ var $_ftp = null;
20
+
21
+ /**
22
+ * PHP5 Constructor
23
+ *
24
+ * @param array $config
25
+ */
26
+ function __construct( $config = array() ) {
27
+ $config = array_merge( array(
28
+ 'host' => '',
29
+ 'type' => '',
30
+ 'user' => '',
31
+ 'pass' => '',
32
+ 'path' => '',
33
+ 'pasv' => false,
34
+ 'domain' => array(),
35
+ 'docroot' => ''
36
+ ), $config );
37
+
38
+ $host_port = explode( ':', $config['host'] );
39
+ if ( sizeof( $host_port ) == 2 ) {
40
+ $config['host'] = $host_port[0];
41
+ $config['port'] = $host_port[1];
42
+ }
43
+
44
+ parent::__construct( $config );
45
+ }
46
+
47
+ /**
48
+ * Connects to FTP server
49
+ *
50
+ * @param string $error
51
+ * @return boolean
52
+ */
53
+ function _connect( &$error ) {
54
+ if ( empty( $this->_config['host'] ) ) {
55
+ $error = 'Empty host.';
56
+
57
+ return false;
58
+ }
59
+
60
+ if ( empty( $this->_config['port'] ) ) {
61
+ $this->_config['port'] = 21;
62
+ }
63
+
64
+ $this->_set_error_handler();
65
+
66
+ if ( $this->_config['type'] == 'ftps' )
67
+ $this->_ftp = @ftp_ssl_connect( $this->_config['host'],
68
+ (int) $this->_config['port'], W3TC_CDN_FTP_CONNECT_TIMEOUT );
69
+ else
70
+ $this->_ftp = @ftp_connect( $this->_config['host'],
71
+ (int) $this->_config['port'], W3TC_CDN_FTP_CONNECT_TIMEOUT );
72
+
73
+ if ( !$this->_ftp ) {
74
+ $error = sprintf( 'Unable to connect to %s:%d (%s).',
75
+ $this->_config['host'], $this->_config['port'],
76
+ $this->_get_last_error() );
77
+
78
+ $this->_restore_error_handler();
79
+
80
+ return false;
81
+ }
82
+
83
+ if ( !@ftp_login( $this->_ftp, $this->_config['user'], $this->_config['pass'] ) ) {
84
+ $error = sprintf( 'Incorrect login or password (%s).', $this->_get_last_error() );
85
+
86
+ $this->_restore_error_handler();
87
+ $this->_disconnect();
88
+
89
+ return false;
90
+ }
91
+
92
+ if ( !@ftp_pasv( $this->_ftp, $this->_config['pasv'] ) ) {
93
+ $error = sprintf( 'Unable to change mode to passive (%s).', $this->_get_last_error() );
94
+
95
+ $this->_restore_error_handler();
96
+ $this->_disconnect();
97
+
98
+ return false;
99
+ }
100
+
101
+ if ( !empty( $this->_config['path'] ) && !@ftp_chdir( $this->_ftp, $this->_config['path'] ) ) {
102
+ $error = sprintf( 'Unable to change directory to: %s (%s).', $this->_config['path'], $this->_get_last_error() );
103
+
104
+ $this->_restore_error_handler();
105
+ $this->_disconnect();
106
+
107
+ return false;
108
+ }
109
+
110
+ $this->_restore_error_handler();
111
+
112
+ return true;
113
+ }
114
+
115
+ /**
116
+ * Disconnects from FTP server
117
+ */
118
+ function _disconnect() {
119
+ @ftp_close( $this->_ftp );
120
+ }
121
+
122
+ /**
123
+ * Sends MDTM command
124
+ *
125
+ * @param string $remote_file
126
+ * @param integer $mtime
127
+ * @return boolean
128
+ */
129
+ function _mdtm( $remote_file, $mtime ) {
130
+ $command = sprintf( 'MDTM %s %s', date( 'YmdHis', $mtime ), $remote_file );
131
+
132
+ return @ftp_raw( $this->_ftp, $command );
133
+ }
134
+
135
+ /**
136
+ * Uploads files to FTP
137
+ *
138
+ * @param array $files
139
+ * @param array $results
140
+ * @param boolean $force_rewrite
141
+ * @return boolean
142
+ */
143
+ function upload( $files, &$results, $force_rewrite = false,
144
+ $timeout_time = NULL ) {
145
+ $error = null;
146
+
147
+ if ( !$this->_connect( $error ) ) {
148
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
149
+
150
+ return false;
151
+ }
152
+
153
+ $this->_set_error_handler();
154
+
155
+ $home = @ftp_pwd( $this->_ftp );
156
+
157
+ if ( $home === false ) {
158
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Unable to get current directory (%s).', $this->_get_last_error() ) );
159
+
160
+ $this->_restore_error_handler();
161
+ $this->_disconnect();
162
+
163
+ return false;
164
+ }
165
+
166
+ foreach ( $files as $file ) {
167
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
168
+ break;
169
+
170
+ $local_path = $file['local_path'];
171
+ $remote_path = $file['remote_path'];
172
+
173
+ if ( !file_exists( $local_path ) ) {
174
+ $results[] = $this->_get_result( $local_path, $remote_path,
175
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
176
+
177
+ continue;
178
+ }
179
+
180
+ @ftp_chdir( $this->_ftp, $home );
181
+
182
+ $remote_dir = dirname( $remote_path );
183
+ $remote_dirs = preg_split( '~\\/+~', $remote_dir );
184
+
185
+ foreach ( $remote_dirs as $dir ) {
186
+ if ( !@ftp_chdir( $this->_ftp, $dir ) ) {
187
+ if ( !@ftp_mkdir( $this->_ftp, $dir ) ) {
188
+ $results[] = $this->_get_result( $local_path,
189
+ $remote_path, W3TC_CDN_RESULT_ERROR,
190
+ sprintf( 'Unable to create directory (%s).',
191
+ $this->_get_last_error() ),
192
+ $file );
193
+
194
+ continue 2;
195
+ }
196
+
197
+ if ( !@ftp_chdir( $this->_ftp, $dir ) ) {
198
+ $results[] = $this->_get_result( $local_path,
199
+ $remote_path, W3TC_CDN_RESULT_ERROR,
200
+ sprintf( 'Unable to change directory (%s).',
201
+ $this->_get_last_error() ),
202
+ $file );
203
+
204
+ continue 2;
205
+ }
206
+ }
207
+ }
208
+
209
+ // basename cannot be used, kills chinese chars and similar characters
210
+ $remote_file = substr( $remote_path, strrpos( $remote_path, '/' )+1 );
211
+
212
+ $mtime = @filemtime( $local_path );
213
+
214
+ if ( !$force_rewrite ) {
215
+ $size = @filesize( $local_path );
216
+ $ftp_size = @ftp_size( $this->_ftp, $remote_file );
217
+ $ftp_mtime = @ftp_mdtm( $this->_ftp, $remote_file );
218
+
219
+ if ( $size === $ftp_size && $mtime === $ftp_mtime ) {
220
+ $results[] = $this->_get_result( $local_path, $remote_path,
221
+ W3TC_CDN_RESULT_OK, 'File up-to-date.', $file );
222
+
223
+ continue;
224
+ }
225
+ }
226
+
227
+ $result = @ftp_put( $this->_ftp, $remote_file, $local_path, FTP_BINARY );
228
+
229
+ if ( $result ) {
230
+ $this->_mdtm( $remote_file, $mtime );
231
+
232
+ $results[] = $this->_get_result( $local_path, $remote_path,
233
+ W3TC_CDN_RESULT_OK, 'OK', $file );
234
+ } else {
235
+ $results[] = $this->_get_result( $local_path, $remote_path,
236
+ W3TC_CDN_RESULT_ERROR,
237
+ sprintf( 'Unable to upload file (%s).',
238
+ $this->_get_last_error() ),
239
+ $file );
240
+ }
241
+ }
242
+
243
+ $this->_restore_error_handler();
244
+ $this->_disconnect();
245
+
246
+ return !$this->_is_error( $results );
247
+ }
248
+
249
+ /**
250
+ * Deletes files from FTP
251
+ *
252
+ * @param array $files
253
+ * @param array $results
254
+ * @return boolean
255
+ */
256
+ function delete( $files, &$results ) {
257
+ $error = null;
258
+
259
+ if ( !$this->_connect( $error ) ) {
260
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
261
+
262
+ return false;
263
+ }
264
+
265
+ $this->_set_error_handler();
266
+
267
+ foreach ( $files as $file ) {
268
+ $local_path = $file['local_path'];
269
+ $remote_path = $file['remote_path'];
270
+
271
+ $result = @ftp_delete( $this->_ftp, $remote_path );
272
+
273
+ if ( $result ) {
274
+ $results[] = $this->_get_result( $local_path, $remote_path,
275
+ W3TC_CDN_RESULT_OK, 'OK', $file );
276
+ } else {
277
+ $results[] = $this->_get_result( $local_path, $remote_path,
278
+ W3TC_CDN_RESULT_ERROR,
279
+ sprintf( 'Unable to delete file (%s).',
280
+ $this->_get_last_error() ),
281
+ $file );
282
+ }
283
+
284
+ while ( true ) {
285
+ $remote_path = dirname( $remote_path );
286
+
287
+ if ( $remote_path == '.' || !@ftp_rmdir( $this->_ftp, $remote_path ) ) {
288
+ break;
289
+ }
290
+ }
291
+ }
292
+
293
+ $this->_restore_error_handler();
294
+ $this->_disconnect();
295
+
296
+ return !$this->_is_error( $results );
297
+ }
298
+
299
+ /**
300
+ * Tests FTP server
301
+ *
302
+ * @param string $error
303
+ * @return boolean
304
+ */
305
+ function test( &$error ) {
306
+ if ( !parent::test( $error ) ) {
307
+ return false;
308
+ }
309
+
310
+ $rand = md5( time() );
311
+ $tmp_dir = 'test_dir_' . $rand;
312
+ $tmp_file = 'test_file_' . $rand;
313
+ $tmp_path = W3TC_CACHE_TMP_DIR . '/' . $tmp_file;
314
+
315
+ if ( !@file_put_contents( $tmp_path, $rand ) ) {
316
+ $error = sprintf( 'Unable to create file: %s.', $tmp_path );
317
+
318
+ return false;
319
+ }
320
+
321
+ if ( !$this->_connect( $error ) ) {
322
+ return false;
323
+ }
324
+
325
+ $this->_set_error_handler();
326
+
327
+ if ( !@ftp_mkdir( $this->_ftp, $tmp_dir ) ) {
328
+ $error = sprintf( 'Unable to make directory: %s (%s).', $tmp_dir, $this->_get_last_error() );
329
+
330
+ @unlink( $tmp_path );
331
+
332
+ $this->_restore_error_handler();
333
+ $this->_disconnect();
334
+
335
+ return false;
336
+ }
337
+
338
+ if ( file_exists( $this->_config['docroot'] . '/' . $tmp_dir ) ) {
339
+ $error = sprintf( 'Test directory was made in your site root, not on separate FTP host or path. Change path or FTP information: %s.', $tmp_dir );
340
+
341
+ @unlink( $tmp_path );
342
+ @ftp_rmdir( $this->_ftp, $tmp_dir );
343
+
344
+ $this->_restore_error_handler();
345
+ $this->_disconnect();
346
+
347
+ return false;
348
+ }
349
+
350
+ if ( !@ftp_chdir( $this->_ftp, $tmp_dir ) ) {
351
+ $error = sprintf( 'Unable to change directory to: %s (%s).', $tmp_dir, $this->_get_last_error() );
352
+
353
+ @unlink( $tmp_path );
354
+ @ftp_rmdir( $this->_ftp, $tmp_dir );
355
+
356
+ $this->_restore_error_handler();
357
+ $this->_disconnect();
358
+
359
+ return false;
360
+ }
361
+
362
+ if ( !@ftp_put( $this->_ftp, $tmp_file, $tmp_path, FTP_BINARY ) ) {
363
+ $error = sprintf( 'Unable to upload file: %s (%s).', $tmp_path, $this->_get_last_error() );
364
+
365
+ @unlink( $tmp_path );
366
+ @ftp_cdup( $this->_ftp );
367
+ @ftp_rmdir( $this->_ftp, $tmp_dir );
368
+
369
+ $this->_restore_error_handler();
370
+ $this->_disconnect();
371
+
372
+ return false;
373
+ }
374
+
375
+ @unlink( $tmp_path );
376
+
377
+ if ( !@ftp_delete( $this->_ftp, $tmp_file ) ) {
378
+ $error = sprintf( 'Unable to delete file: %s (%s).', $tmp_path, $this->_get_last_error() );
379
+
380
+ @ftp_cdup( $this->_ftp );
381
+ @ftp_rmdir( $this->_ftp, $tmp_dir );
382
+
383
+ $this->_restore_error_handler();
384
+ $this->_disconnect();
385
+
386
+ return false;
387
+ }
388
+
389
+ @ftp_cdup( $this->_ftp );
390
+
391
+ if ( !@ftp_rmdir( $this->_ftp, $tmp_dir ) ) {
392
+ $error = sprintf( 'Unable to remove directory: %s (%s).', $tmp_dir, $this->_get_last_error() );
393
+
394
+ $this->_restore_error_handler();
395
+ $this->_disconnect();
396
+
397
+ return false;
398
+ }
399
+
400
+ $this->_restore_error_handler();
401
+ $this->_disconnect();
402
+
403
+ return true;
404
+ }
405
+
406
+ /**
407
+ * Returns array of CDN domains
408
+ *
409
+ * @return array
410
+ */
411
+ function get_domains() {
412
+ if ( !empty( $this->_config['domain'] ) ) {
413
+ return (array) $this->_config['domain'];
414
+ }
415
+
416
+ return array();
417
+ }
418
+
419
+ /**
420
+ * How and if headers should be set
421
+ *
422
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
423
+ */
424
+ function headers_support() {
425
+ return W3TC_CDN_HEADER_MIRRORING;
426
+ }
427
+ }
CdnEngine_GoogleDrive.php ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Google drive engine
6
+ */
7
+ class CdnEngine_GoogleDrive extends CdnEngine_Base {
8
+ private $_client_id;
9
+ private $_refresh_token;
10
+ private $_root_folder_id;
11
+ private $_root_url;
12
+
13
+ private $_service;
14
+
15
+
16
+
17
+ /**
18
+ * PHP5 Constructor
19
+ *
20
+ * @param array $config
21
+ */
22
+ function __construct( $config = array() ) {
23
+ parent::__construct( $config );
24
+
25
+ $this->_client_id = $config['client_id'];
26
+ $this->_refresh_token = $config['refresh_token'];
27
+ $this->_root_folder_id = $config['root_folder_id'];
28
+ $this->_root_url = rtrim( $config['root_url'], '/' ) . '/';
29
+ $this->_new_access_token_callback = $config['new_access_token_callback'];
30
+
31
+ $this->_init_service( $config['access_token'] );
32
+ }
33
+
34
+
35
+
36
+ private function _init_service( $access_token ) {
37
+ $client = new \Google_Client();
38
+ $client->setClientId( $this->_client_id );
39
+ $client->setAccessToken( $access_token );
40
+ $this->_service = new \Google_Service_Drive( $client );
41
+ }
42
+
43
+
44
+
45
+ private function _refresh_token() {
46
+ $result = wp_remote_post( GOOGLE_DRIVE_AUTHORIZE_URL, array(
47
+ 'body' => array(
48
+ 'client_id' => $this->_client_id,
49
+ 'refresh_token' => $this->_refresh_token
50
+ ) ) );
51
+
52
+ if ( is_wp_error( $result ) )
53
+ throw new \Exception( $result );
54
+ elseif ( $result['response']['code'] != '200' )
55
+ throw new \Exception( $result['body'] );
56
+
57
+ $access_token = $result['body'];
58
+ call_user_func( $this->_new_access_token_callback, $access_token );
59
+ $this->_init_service( $access_token );
60
+ }
61
+
62
+
63
+
64
+ /**
65
+ * Uploads files
66
+ *
67
+ * @param array $files
68
+ * @param array $results
69
+ * @param boolean $force_rewrite
70
+ * @return boolean
71
+ */
72
+ function upload( $files, &$results, $force_rewrite = false, $timeout_time = NULL ) {
73
+ $allow_refresh_token = true;
74
+ $result = true;
75
+
76
+ $files_chunks = array_chunk( $files , 20 );
77
+ foreach ( $files_chunks as $files_chunk ) {
78
+ $r = $this->_upload_chunk( $files_chunk, $results,
79
+ $force_rewrite, $timeout_time, $allow_refresh_token );
80
+ if ( $r == 'refresh_required' ) {
81
+ $allow_refresh_token = false;
82
+ $this->_refresh_token();
83
+
84
+ $r = $this->_upload_chunk( $files_chunk, $results,
85
+ $force_rewrite, $timeout_time, $allow_refresh_token );
86
+ }
87
+ if ( $r != 'success' )
88
+ $result = false;
89
+ if ( $r == 'timeout' )
90
+ break;
91
+ }
92
+
93
+ return $result;
94
+ }
95
+
96
+
97
+
98
+ private function _upload_chunk( $files, &$results, $force_rewrite,
99
+ $timeout_time, $allow_refresh_token ) {
100
+ list( $result, $listed_files ) = $this->list_files_chunk( $files,
101
+ $allow_refresh_token, $timeout_time );
102
+ if ( $result != 'success' )
103
+ return $result;
104
+
105
+ // remove dups
106
+ $files_by_title = array();
107
+
108
+ for ( $n = 0; $n < count( $listed_files ); $n++ ) {
109
+ $title_to_search = $listed_files[$n]->title;
110
+ $files_by_title[$title_to_search] = $listed_files[$n];
111
+
112
+ for ( $m = $n + 1; $m < count( $listed_files ); $m++ ) {
113
+ if ( $listed_files[$m]->title == $title_to_search ) {
114
+ try {
115
+ $this->_service->files->delete( $listed_files[$m]->id );
116
+ } catch ( \Google_Service_Exception $e ) {
117
+ $errors = $e->getErrors();
118
+ $details = '';
119
+ if ( count( $errors ) >= 1 ) {
120
+ if ( $errors[0]['reason'] == 'notFound' ) {
121
+ continue;
122
+ } else
123
+ $details = $errors[0]['reason'];
124
+ }
125
+
126
+ $results[] = $this->_get_result( '',
127
+ '', W3TC_CDN_RESULT_ERROR,
128
+ 'Failed to delete dup file ' . $title_to_search . ' ' . $details );
129
+ $result = 'with_errors';
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ // check update date and upload
136
+ foreach ( $files as $file_descriptor ) {
137
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
138
+ return 'timeout';
139
+
140
+ list( $parent_id, $title ) = $this->remote_path_to_title(
141
+ $file_descriptor['remote_path'] );
142
+ $properties = array();
143
+
144
+ if ( isset( $file_descriptor['content'] ) ) {
145
+ // when content specified - just upload
146
+ $content = $file_descriptor['content'];
147
+ } else {
148
+ $local_path = $file_descriptor['local_path'];
149
+ if ( !file_exists( $local_path ) ) {
150
+ $results[] = $this->_get_result( $local_path,
151
+ $file_descriptor['remote_path'],
152
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.',
153
+ $file_descriptor );
154
+ continue;
155
+ }
156
+
157
+ $mtime = @filemtime( $local_path );
158
+
159
+ $p = new \Google_Service_Drive_Property();
160
+ $p->key = 'mtime';
161
+ $p->value = $mtime;
162
+ $properties[] = $p;
163
+
164
+ if ( !$force_rewrite && isset( $files_by_title[$title] ) ) {
165
+ $existing_file = $files_by_title[$title];
166
+ $existing_size = $existing_file->fileSize;
167
+ $existing_mtime = 0;
168
+ if ( is_array( $existing_file->properties ) ) {
169
+ foreach ( $existing_file->properties as $p ) {
170
+ if ( $p->key == 'mtime' )
171
+ $existing_mtime = $p->value;
172
+ }
173
+ }
174
+
175
+ $size = @filesize( $local_path );
176
+ if ( $mtime == $existing_mtime && $size == $existing_size ) {
177
+ $results[] = $this->_get_result( $file_descriptor['local_path'],
178
+ $file_descriptor['remote_path'], W3TC_CDN_RESULT_OK,
179
+ 'File up-to-date.', $file_descriptor );
180
+ continue;
181
+ }
182
+ }
183
+
184
+ $content = file_get_contents( $local_path );
185
+ }
186
+
187
+ $file = new \Google_Service_Drive_DriveFile();
188
+ $file->setTitle( $title );
189
+ $file->setProperties( $properties );
190
+
191
+ $parent = new \Google_Service_Drive_ParentReference();
192
+ $parent->setId( $parent_id );
193
+ $file->setParents( array( $parent ) );
194
+
195
+ try {
196
+ try {
197
+ // update file if there's one already or insert
198
+ if ( isset( $files_by_title[$title] ) ) {
199
+ $existing_file = $files_by_title[$title];
200
+
201
+ $created_file = $this->_service->files->update(
202
+ $existing_file->id, $file, array(
203
+ 'data' => $content,
204
+ 'uploadType' => 'media'
205
+ ) );
206
+ } else {
207
+ $created_file = $this->_service->files->insert( $file, array(
208
+ 'data' => $content,
209
+ 'uploadType' => 'media'
210
+ ) );
211
+
212
+ $permission = new \Google_Service_Drive_Permission();
213
+ $permission->setValue( '' );
214
+ $permission->setType( 'anyone' );
215
+ $permission->setRole( 'reader' );
216
+
217
+ $this->_service->permissions->insert( $created_file->id,
218
+ $permission );
219
+ }
220
+ } catch ( \Google_Auth_Exception $e ) {
221
+ if ( $allow_refresh_token )
222
+ return 'refresh_required';
223
+
224
+ throw $e;
225
+ }
226
+
227
+ $results[] = $this->_get_result( $file_descriptor['local_path'],
228
+ $file_descriptor['remote_path'], W3TC_CDN_RESULT_OK,
229
+ 'OK', $file_descriptor );
230
+ } catch ( \Google_Service_Exception $e ) {
231
+ $errors = $e->getErrors();
232
+ $details = '';
233
+ if ( count( $errors ) >= 1 ) {
234
+ $details = $errors[0]['reason'];
235
+ }
236
+
237
+ delete_transient( 'w3tc_cdn_google_drive_folder_ids' );
238
+
239
+ $results[] = $this->_get_result( $file_descriptor['local_path'],
240
+ $file_descriptor['remote_path'], W3TC_CDN_RESULT_ERROR,
241
+ 'Failed to upload file ' . $file_descriptor['remote_path'] .
242
+ ' ' . $details, $file_descriptor );
243
+ $result = 'with_errors';
244
+ continue;
245
+ } catch ( \Exception $e ) {
246
+ delete_transient( 'w3tc_cdn_google_drive_folder_ids' );
247
+
248
+ $results[] = $this->_get_result( $file_descriptor['local_path'],
249
+ $file_descriptor['remote_path'], W3TC_CDN_RESULT_ERROR,
250
+ 'Failed to upload file ' . $file_descriptor['remote_path'],
251
+ $file_descriptor );
252
+ $result = 'with_errors';
253
+ continue;
254
+ }
255
+ }
256
+
257
+ return $result;
258
+ }
259
+
260
+
261
+
262
+ /**
263
+ * Deletes files
264
+ *
265
+ * @param array $files
266
+ * @param array $results
267
+ * @return boolean
268
+ */
269
+ function delete( $files, &$results ) {
270
+ $allow_refresh_token = true;
271
+ $result = true;
272
+
273
+ $files_chunks = array_chunk( $files , 20 );
274
+ foreach ( $files_chunks as $files_chunk ) {
275
+ $r = $this->_delete_chunk( $files_chunk, $results,
276
+ $allow_refresh_token );
277
+ if ( $r == 'refresh_required' ) {
278
+ $allow_refresh_token = false;
279
+ $this->_refresh_token();
280
+
281
+ $r = $this->_delete_chunk( $files_chunk, $results,
282
+ $allow_refresh_token );
283
+ }
284
+ if ( $r != 'success' )
285
+ $result = false;
286
+ }
287
+
288
+ return $result;
289
+ }
290
+
291
+
292
+
293
+ private function _delete_chunk( $files, &$results, $allow_refresh_token ) {
294
+ list( $result, $listed_files ) = $this->list_files_chunk( $files,
295
+ $allow_refresh_token );
296
+ if ( $result != 'success' )
297
+ return $result;
298
+
299
+ foreach ( $listed_files->items as $item ) {
300
+ try {
301
+ $this->_service->files->delete( $item->id );
302
+
303
+ $results[] = $this->_get_result( $item->title,
304
+ $item->title, W3TC_CDN_RESULT_OK,
305
+ 'OK' );
306
+ } catch ( \Exception $e ) {
307
+ $results[] = $this->_get_result( '',
308
+ '', W3TC_CDN_RESULT_ERROR,
309
+ 'Failed to delete file ' . $item->title );
310
+ $result = 'with_errors';
311
+ continue;
312
+ }
313
+
314
+ }
315
+
316
+ return $result;
317
+ }
318
+
319
+
320
+
321
+
322
+ private function list_files_chunk( $files, $allow_refresh_token,
323
+ $timeout_time = NULL ) {
324
+ $titles_filter = array();
325
+
326
+ try {
327
+ foreach ( $files as $file_descriptor ) {
328
+ list( $parent_id, $title ) = $this->remote_path_to_title(
329
+ $file_descriptor['remote_path'] );
330
+ $titles_filter[] = '("' . $parent_id .
331
+ '" in parents and title = "' . $title . '")';
332
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
333
+ return array( 'timeout', array() );
334
+ }
335
+ } catch ( \Google_Auth_Exception $e ) {
336
+ if ( $allow_refresh_token )
337
+ return array( 'refresh_required', array() );
338
+
339
+ throw $e;
340
+ } catch ( \Exception $e ) {
341
+ return array( 'with_errors', array() );
342
+ }
343
+
344
+
345
+ // find files
346
+ try {
347
+ try {
348
+ $listed_files = $this->_service->files->listFiles(
349
+ array(
350
+ 'q' =>
351
+ '(' . join( $titles_filter, ' or ' ) . ') ' .
352
+ 'and trashed = false'
353
+ )
354
+ );
355
+ } catch ( \Google_Auth_Exception $e ) {
356
+ if ( $allow_refresh_token )
357
+ return array( 'refresh_required', array() );
358
+
359
+ throw $e;
360
+ }
361
+ } catch ( \Exception $e ) {
362
+ return array( 'with_errors', array() );
363
+ }
364
+
365
+ return array( 'success', $listed_files );
366
+ }
367
+
368
+
369
+
370
+ private function remote_path_to_title( $remote_path ) {
371
+ $title = substr( $remote_path, 1 );
372
+ $pos = strrpos( $remote_path, '/' );
373
+ if ( $pos === false ) {
374
+ $path = '';
375
+ $title = $remote_path;
376
+ } else {
377
+ $path = substr( $remote_path, 0, $pos );
378
+ $title = substr( $remote_path, $pos + 1 );
379
+ }
380
+
381
+ $title = str_replace( '"', "'", $title );
382
+ $parent_id = $this->path_to_parent_id( $this->_root_folder_id, $path );
383
+
384
+ return array( $parent_id, $title );
385
+ }
386
+
387
+
388
+
389
+ private function path_to_parent_id( $root_id, $path ) {
390
+ if ( empty( $path ) )
391
+ return $root_id;
392
+
393
+ $path = ltrim( $path, '/' );
394
+ $pos = strpos( $path, '/' );
395
+ if ( $pos === false ) {
396
+ $top_folder = $path;
397
+ $remaining_path = '';
398
+ } else {
399
+ $top_folder = substr( $path, 0, $pos );
400
+ $remaining_path = substr( $path, $pos + 1 );
401
+ }
402
+
403
+ $new_root_id = $this->parent_id_resolve_step( $root_id, $top_folder );
404
+ return $this->path_to_parent_id( $new_root_id, $remaining_path );
405
+ }
406
+
407
+
408
+
409
+ private function parent_id_resolve_step( $root_id, $folder ) {
410
+ // decode top folder
411
+ $ids_string = get_transient( 'w3tc_cdn_google_drive_folder_ids' );
412
+ $ids = @unserialize( $ids_string );
413
+
414
+ if ( isset( $ids[$root_id . '_' . $folder] ) )
415
+ return $ids[$root_id . '_' . $folder];
416
+
417
+ // find folder
418
+ $items = $this->_service->files->listFiles( array(
419
+ 'q' => '"' . $root_id . '" in parents '.
420
+ 'and title = "' . $folder . '" ' .
421
+ 'and mimeType = "application/vnd.google-apps.folder" ' .
422
+ 'and trashed = false'
423
+ ) );
424
+
425
+ if ( count( $items ) > 0 ) {
426
+ $id = $items[0]->id;
427
+ } else {
428
+ // create folder
429
+ $file = new \Google_Service_Drive_DriveFile( array(
430
+ 'title' => $folder,
431
+ 'mimeType' => 'application/vnd.google-apps.folder' ) );
432
+
433
+ $parent = new \Google_Service_Drive_ParentReference();
434
+ $parent->setId( $root_id );
435
+ $file->setParents( array( $parent ) );
436
+
437
+ $created_file = $this->_service->files->insert( $file );
438
+ $id = $created_file->id;
439
+
440
+ $permission = new \Google_Service_Drive_Permission();
441
+ $permission->setValue( '' );
442
+ $permission->setType( 'anyone' );
443
+ $permission->setRole( 'reader' );
444
+
445
+ $this->_service->permissions->insert( $id, $permission );
446
+ }
447
+
448
+ if ( !is_array( $ids ) )
449
+ $ids = array();
450
+ $ids[$root_id . '_' . $folder] = $id;
451
+ set_transient( 'w3tc_cdn_google_drive_folder_ids', serialize( $ids ) );
452
+
453
+ return $id;
454
+ }
455
+
456
+
457
+
458
+ /**
459
+ * Tests
460
+ *
461
+ * @param string $error
462
+ * @return boolean
463
+ */
464
+ function test( &$error ) {
465
+ $test_content = '' . rand();
466
+
467
+ $file = array(
468
+ 'local_path' => 'n/a',
469
+ 'remote_path' => '/folder/test.txt',
470
+ 'content' => $test_content
471
+ );
472
+ $results = array();
473
+
474
+ if ( !$this->upload( array( $file ), $results ) ) {
475
+ $error = sprintf( 'Unable to upload file %s', $file['remote_path'] );
476
+ return false;
477
+ }
478
+ if ( !$this->delete( array( $file ), $results ) ) {
479
+ $error = sprintf( 'Unable to delete file %s', $file['remote_path'] );
480
+ return false;
481
+ }
482
+
483
+ return true;
484
+ }
485
+
486
+
487
+
488
+ /**
489
+ * Returns array of CDN domains
490
+ *
491
+ * @return array
492
+ */
493
+ function get_domains() {
494
+ return array();
495
+ }
496
+
497
+
498
+
499
+ /**
500
+ * How and if headers should be set
501
+ *
502
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
503
+ */
504
+ function headers_support() {
505
+ return W3TC_CDN_HEADER_NONE;
506
+ }
507
+
508
+
509
+ function format_url( $path ) {
510
+ return $this->_root_url . ltrim( $path, '/' );
511
+ }
512
+ }
CdnEngine_Mirror.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Mirror Class
6
+ */
7
+ class CdnEngine_Mirror extends CdnEngine_Base {
8
+ /**
9
+ * PHP5 Constructor
10
+ *
11
+ * @param array $config
12
+ */
13
+ function __construct( $config = array() ) {
14
+ $config = array_merge( array(
15
+ 'domain' => array(),
16
+ ), $config );
17
+
18
+ parent::__construct( $config );
19
+ }
20
+
21
+ /**
22
+ * Uploads files stub
23
+ *
24
+ * @param array $files
25
+ * @param array $results
26
+ * @param boolean $force_rewrite
27
+ * @return boolean
28
+ */
29
+ function upload( $files, &$results, $force_rewrite = false,
30
+ $timeout_time = NULL ) {
31
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
32
+
33
+ return true;
34
+ }
35
+
36
+ /**
37
+ * Deletes files stub
38
+ *
39
+ * @param array $files
40
+ * @param array $results
41
+ * @return boolean
42
+ */
43
+ function delete( $files, &$results ) {
44
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
45
+
46
+ return true;
47
+ }
48
+
49
+ /**
50
+ * Tests mirror
51
+ *
52
+ * @param string $error
53
+ * @return bool
54
+ */
55
+ function test( &$error ) {
56
+ if ( !parent::test( $error ) ) {
57
+ return false;
58
+ }
59
+
60
+ $results = array();
61
+ $files = array(
62
+ array(
63
+ 'local_path' => '',
64
+ 'remote_path' => 'purge_test_' . time()
65
+ ) );
66
+
67
+ if ( !$this->purge( $files, $results ) && isset( $results[0]['error'] ) ) {
68
+ $error = $results[0]['error'];
69
+
70
+ return false;
71
+ }
72
+
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * Returns array of CDN domains
78
+ *
79
+ * @return array
80
+ */
81
+ function get_domains() {
82
+ if ( !empty( $this->_config['domain'] ) ) {
83
+ return (array) $this->_config['domain'];
84
+ }
85
+
86
+ return array();
87
+ }
88
+
89
+ /**
90
+ * How and if headers should be set
91
+ *
92
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
93
+ */
94
+ function headers_support() {
95
+ return W3TC_CDN_HEADER_MIRRORING;
96
+ }
97
+ }
CdnEngine_Mirror_Akamai.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Netdna Class
6
+ */
7
+
8
+ define( 'W3TC_CDN_MIRROR_AKAMAI_WSDL', 'https://ccuapi.akamai.com/ccuapi-axis.wsdl' );
9
+ define( 'W3TC_CDN_MIRROR_AKAMAI_NAMESPACE', 'http://www.akamai.com/purge' );
10
+
11
+ class CdnEngine_Mirror_Akamai extends CdnEngine_Mirror {
12
+ /**
13
+ * PHP5 Constructor
14
+ *
15
+ * @param array $config
16
+ */
17
+ function __construct( $config = array() ) {
18
+ $config = array_merge( array(
19
+ 'username' => '',
20
+ 'password' => '',
21
+ 'zone' => '',
22
+ 'action' => 'invalidate',
23
+ 'email_notification' => array()
24
+ ), $config );
25
+
26
+ parent::__construct( $config );
27
+ }
28
+
29
+ /**
30
+ * Purges remote files
31
+ *
32
+ * @param array $files
33
+ * @param array $results
34
+ * @return boolean
35
+ */
36
+ function purge( $files, &$results ) {
37
+ if ( empty( $this->_config['username'] ) ) {
38
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty username.', 'w3-total-cache' ) );
39
+
40
+ return false;
41
+ }
42
+
43
+ if ( empty( $this->_config['password'] ) ) {
44
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty password.', 'w3-total-cache' ) );
45
+
46
+ return false;
47
+ }
48
+
49
+ require_once W3TC_LIB_DIR . '/Nusoap/nusoap.php';
50
+
51
+ $client = new \nusoap_client(
52
+ W3TC_CDN_MIRROR_AKAMAI_WSDL,
53
+ 'wsdl'
54
+ );
55
+
56
+ $error = $client->getError();
57
+
58
+ if ( $error ) {
59
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( __( 'Constructor error (%s).', 'w3-total-cache' ), $error ) );
60
+
61
+ return false;
62
+ }
63
+
64
+ $zone = $this->_config['zone'];
65
+
66
+ $expressions = array();
67
+ foreach ( $files as $file ) {
68
+ $remote_path = $file['remote_path'];
69
+ $expressions[] = $this->_format_url( $remote_path );
70
+ }
71
+
72
+ $action = $this->_config['action'];
73
+ $email = $this->_config['email_notification'];
74
+
75
+ $email = implode( ',', $email );
76
+ $options = array( 'action='.$action, 'domain='.$zone, 'type=arl' );
77
+ if ( $email )
78
+ $options[] = 'email-notification='.$email;
79
+ $params = array( $this->_config['username'],
80
+ $this->_config['password'],
81
+ '',
82
+ $options,
83
+ $expressions
84
+ );
85
+
86
+ $result = $client->call( 'purgeRequest', $params, W3TC_CDN_MIRROR_AKAMAI_NAMESPACE );
87
+
88
+ if ( $client->fault ) {
89
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Invalid response.', 'w3-total-cache' ) );
90
+
91
+ return false;
92
+ }
93
+ $result_code = $result['resultCode'];
94
+ $result_message = $result['resultMsg'];
95
+
96
+ $error = $client->getError();
97
+
98
+ if ( $error ) {
99
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( __( 'Unable to purge (%s).', 'w3-total-cache' ), $error ) );
100
+
101
+ return false;
102
+ }
103
+ if ( $result_code>=300 ) {
104
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( __( 'Unable to purge (%s).', 'w3-total-cache' ), $result_message ) );
105
+
106
+ return false;
107
+ }
108
+
109
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ) );
110
+
111
+ return true;
112
+ }
113
+ }
CdnEngine_Mirror_Att.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ define( 'W3TC_CDN_EDGECAST_PURGE_URL', 'http://api.acdn.att.com/v2/mcc/customers/%s/edge/purge' );
5
+
6
+ /**
7
+ * class CdnEngine_Mirror_Att
8
+ */
9
+ class CdnEngine_Mirror_Att extends CdnEngine_Mirror_Edgecast {
10
+
11
+ }
CdnEngine_Mirror_Cotendo.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Netdna Class
6
+ */
7
+
8
+ define( 'W3TC_CDN_MIRROR_COTENDO_WSDL', 'https://api.cotendo.net/cws?wsdl' );
9
+ define( 'W3TC_CDN_MIRROR_COTENDO_ENDPOINT', 'http://api.cotendo.net/cws?ver=1.0' );
10
+ define( 'W3TC_CDN_MIRROR_COTENDO_NAMESPACE', 'http://api.cotendo.net/' );
11
+
12
+ /**
13
+ * class CdnEngine_Mirror_Cotendo
14
+ */
15
+ class CdnEngine_Mirror_Cotendo extends CdnEngine_Mirror {
16
+ /**
17
+ * PHP5 Constructor
18
+ *
19
+ * @param array $config
20
+ */
21
+ function __construct( $config = array() ) {
22
+ $config = array_merge( array(
23
+ 'username' => '',
24
+ 'password' => '',
25
+ 'zones' => array(),
26
+ ), $config );
27
+
28
+ parent::__construct( $config );
29
+ }
30
+
31
+ /**
32
+ * Purges remote files
33
+ *
34
+ * @param array $files
35
+ * @param array $results
36
+ * @return boolean
37
+ */
38
+ function purge( $files, &$results ) {
39
+ if ( empty( $this->_config['username'] ) ) {
40
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty username.', 'w3-total-cache' ) );
41
+
42
+ return false;
43
+ }
44
+
45
+ if ( empty( $this->_config['password'] ) ) {
46
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty password.', 'w3-total-cache' ) );
47
+
48
+ return false;
49
+ }
50
+
51
+ if ( empty( $this->_config['zones'] ) ) {
52
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty zones list.', 'w3-total-cache' ) );
53
+
54
+ return false;
55
+ }
56
+
57
+ require_once W3TC_LIB_DIR . '/Nusoap/nusoap.php';
58
+
59
+ $client = new \nusoap_client(
60
+ W3TC_CDN_MIRROR_COTENDO_WSDL,
61
+ 'wsdl'
62
+ );
63
+
64
+ $error = $client->getError();
65
+
66
+ if ( $error ) {
67
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( __( 'Constructor error (%s).', 'w3-total-cache' ), $error ) );
68
+
69
+ return false;
70
+ }
71
+
72
+ $client->authtype = 'basic';
73
+ $client->username = $this->_config['username'];
74
+ $client->password = $this->_config['password'];
75
+ $client->forceEndpoint = W3TC_CDN_MIRROR_COTENDO_ENDPOINT;
76
+
77
+ foreach ( (array) $this->_config['zones'] as $zone ) {
78
+ $expressions = array();
79
+
80
+ foreach ( $files as $file ) {
81
+ $remote_path = $file['remote_path'];
82
+ $expressions[] = '/' . $remote_path;
83
+ }
84
+
85
+ $expression = implode( "\n", $expressions );
86
+
87
+ $params = array(
88
+ 'cname' => $zone,
89
+ 'flushExpression' => $expression,
90
+ 'flushType' => 'hard',
91
+ );
92
+
93
+ $client->call( 'doFlush', $params, W3TC_CDN_MIRROR_COTENDO_NAMESPACE );
94
+
95
+ if ( $client->fault ) {
96
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Invalid response.', 'w3-total-cache' ) );
97
+
98
+ return false;
99
+ }
100
+
101
+ $error = $client->getError();
102
+
103
+ if ( $error ) {
104
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( __( 'Unable to purge (%s).', 'w3-total-cache' ), $error ) );
105
+
106
+ return false;
107
+ }
108
+ }
109
+
110
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ) );
111
+
112
+ return true;
113
+ }
114
+
115
+ /**
116
+ * Purges CDN completely
117
+ *
118
+ * @param unknown $results
119
+ * @return bool
120
+ */
121
+ function purge_all( &$results ) {
122
+ return $this->purge( array( array( 'local_path'=>'*', 'remote_path'=> '*' ) ), $results );
123
+ }
124
+ }
CdnEngine_Mirror_Edgecast.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Netdna Class
6
+ */
7
+
8
+ if ( !defined( 'W3TC_CDN_EDGECAST_PURGE_URL' ) ) define( 'W3TC_CDN_EDGECAST_PURGE_URL', 'http://api.edgecast.com/v2/mcc/customers/%s/edge/purge' );
9
+ define( 'W3TC_CDN_EDGECAST_MEDIATYPE_WINDOWS_MEDIA_STREAMING', 1 );
10
+ define( 'W3TC_CDN_EDGECAST_MEDIATYPE_FLASH_MEDIA_STREAMING', 2 );
11
+ define( 'W3TC_CDN_EDGECAST_MEDIATYPE_HTTP_LARGE_OBJECT', 3 );
12
+ define( 'W3TC_CDN_EDGECAST_MEDIATYPE_HTTP_SMALL_OBJECT', 8 );
13
+ define( 'W3TC_CDN_EDGECAST_MEDIATYPE_APPLICATION_DELIVERY_NETWORK', 14 );
14
+
15
+ /**
16
+ * class CdnEngine_Mirror_Edgecast
17
+ */
18
+ class CdnEngine_Mirror_Edgecast extends CdnEngine_Mirror {
19
+ /**
20
+ * PHP5 Constructor
21
+ *
22
+ * @param array $config
23
+ */
24
+ function __construct( $config = array() ) {
25
+ $config = array_merge( array(
26
+ 'apiid' => '',
27
+ 'apikey' => ''
28
+ ), $config );
29
+
30
+ parent::__construct( $config );
31
+ }
32
+
33
+ /**
34
+ * Purges remote files
35
+ *
36
+ * @param array $files
37
+ * @param array $results
38
+ * @return boolean
39
+ */
40
+ function purge( $files, &$results ) {
41
+ if ( empty( $this->_config['account'] ) ) {
42
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty account #.', 'w3-total-cache' ) );
43
+
44
+ return false;
45
+ }
46
+
47
+ if ( empty( $this->_config['token'] ) ) {
48
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty token.', 'w3-total-cache' ) );
49
+
50
+ return false;
51
+ }
52
+
53
+ foreach ( $files as $file ) {
54
+ $local_path = $file['local_path'];
55
+ $remote_path = $file['remote_path'];
56
+
57
+ $url = $this->format_url( $remote_path );
58
+
59
+ $error = null;
60
+
61
+ if ( $this->_purge_content( $url, W3TC_CDN_EDGECAST_MEDIATYPE_HTTP_SMALL_OBJECT, $error ) ) {
62
+ $results[] = $this->_get_result( $local_path, $remote_path,
63
+ W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ), $file );
64
+ } else {
65
+ $results[] = $this->_get_result( $local_path, $remote_path,
66
+ W3TC_CDN_RESULT_ERROR,
67
+ sprintf( __( 'Unable to purge (%s).', 'w3-total-cache' ), $error ),
68
+ $file );
69
+ }
70
+ }
71
+
72
+ return !$this->_is_error( $results );
73
+ }
74
+
75
+ /**
76
+ * Purges CDN completely
77
+ *
78
+ * @param unknown $results
79
+ * @return bool
80
+ */
81
+ function purge_all( &$results ) {
82
+ return $this->purge( array( array( 'local_path'=>'*', 'remote_path'=> '*' ) ), $results );
83
+ }
84
+
85
+ /**
86
+ * Purge content
87
+ *
88
+ * @param string $path
89
+ * @param int $type
90
+ * @param string $error
91
+ * @return boolean
92
+ */
93
+ function _purge_content( $path, $type, &$error ) {
94
+ $url = sprintf( W3TC_CDN_EDGECAST_PURGE_URL, $this->_config['account'] );
95
+ $args = array(
96
+ 'method' => 'PUT',
97
+ 'user-agent' => W3TC_POWERED_BY,
98
+ 'headers' => array(
99
+ 'Accept' => 'application/json',
100
+ 'Content-Type' => 'application/json',
101
+ 'Authorization' => sprintf( 'TOK:%s', $this->_config['token'] )
102
+ ),
103
+ 'body' => json_encode( array(
104
+ 'MediaPath' => $path,
105
+ 'MediaType' => $type
106
+ ) )
107
+ );
108
+
109
+ $response = wp_remote_request( $url, $args );
110
+
111
+ if ( is_wp_error( $response ) ) {
112
+ $error = implode( '; ', $response->get_error_messages() );
113
+
114
+ return false;
115
+ }
116
+
117
+ switch ( $response['response']['code'] ) {
118
+ case 200:
119
+ return true;
120
+
121
+ case 400:
122
+ $error = __( 'Invalid Request Parameter', 'w3-total-cache' );
123
+ return false;
124
+
125
+ case 403:
126
+ $error = __( 'Authentication Failure or Insufficient Access Rights', 'w3-total-cache' );
127
+ return false;
128
+
129
+ case 404:
130
+ $error = __( 'Invalid Request URI', 'w3-total-cache' );
131
+ return false;
132
+
133
+ case 405:
134
+ $error = __( 'Invalid Request', 'w3-total-cache' );
135
+ return false;
136
+
137
+ case 500:
138
+ $error = __( 'Server Error', 'w3-total-cache' );
139
+ return false;
140
+ }
141
+
142
+ $error = 'Unknown error';
143
+
144
+ return false;
145
+ }
146
+ }
CdnEngine_Mirror_Highwinds.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class CdnEngine_Mirror_Highwinds
6
+ */
7
+ class CdnEngine_Mirror_Highwinds extends CdnEngine_Mirror {
8
+ private $api;
9
+ private $domains;
10
+ private $host_hash_code;
11
+
12
+ /**
13
+ * PHP5 Constructor
14
+ *
15
+ * @param array $config
16
+ * account_hash
17
+ * username
18
+ * password
19
+ * host_hash_code
20
+ */
21
+ function __construct( $config = array() ) {
22
+ $this->api = new Cdn_Highwinds_Api( $config['account_hash'],
23
+ $config['api_token'] );
24
+ $this->host_hash_code = $config['host_hash_code'];
25
+
26
+ if ( !empty( $config['domains'] ) )
27
+ $this->domains = (array)$config['domains'];
28
+ else
29
+ $this->domains = array(
30
+ 'cds.' . $config['host_hash_code'] . '.hwcdn.net' );
31
+
32
+ parent::__construct( $config );
33
+ }
34
+
35
+ /**
36
+ * Purges remote files
37
+ *
38
+ * @param array $files
39
+ * @param array $results
40
+ * @return boolean
41
+ */
42
+ function purge( $files, &$results ) {
43
+ $results = array();
44
+ try {
45
+ $urls = array();
46
+ foreach ( $files as $file )
47
+ $urls[] = $this->_format_url( $file['remote_path'] );
48
+ $this->api->purge( $urls, false );
49
+
50
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' );
51
+ } catch ( \Exception $e ) {
52
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT,
53
+ __( 'Failed to purge: ', 'w3-total-cache' ) . $e->getMessage() );
54
+ }
55
+
56
+ return !$this->_is_error( $results );
57
+ }
58
+
59
+ /**
60
+ * Purge CDN completely
61
+ *
62
+ * @param unknown $results
63
+ * @return bool
64
+ */
65
+ function purge_all( &$results ) {
66
+ $results = array();
67
+ try {
68
+ $urls = array();
69
+ foreach ( $this->domains as $domain ) {
70
+ $urls[] = 'http://' . $domain . '/';
71
+ $urls[] = 'https://' . $domain . '/';
72
+ }
73
+
74
+ $this->api->purge( $urls, true );
75
+
76
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' );
77
+ } catch ( \Exception $e ) {
78
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT,
79
+ __( 'Failed to purge all: ', 'w3-total-cache' ) . $e->getMessage() );
80
+ }
81
+
82
+ return !$this->_is_error( $results );
83
+ }
84
+
85
+
86
+
87
+ function get_domains() {
88
+ return $this->domains;
89
+ }
90
+
91
+
92
+
93
+ public function service_analytics_transfer() {
94
+ $start_date = gmdate( 'Y-m-d', strtotime( '-30 days', time() ) ) . 'T00:00:00Z';
95
+ $end_date = gmdate( 'Y-m-d' ) . 'T00:00:00Z';
96
+
97
+ $response = $this->api->analytics_transfer( $this->host_hash_code,
98
+ 'P1D', 'CDS', $start_date, $end_date );
99
+ if ( !isset( $response['series'] ) || !is_array( $response['series'] ) ||
100
+ count( $response['series'] ) < 1 )
101
+ throw new \Exception( 'cant parse response' );
102
+
103
+ $series = $response['series'][0];
104
+ if ( !isset( $series['metrics'] ) || !is_array( $series['metrics'] ) )
105
+ throw new \Exception( 'cant parse response - no metrics' );
106
+
107
+ $metrics = $series['metrics'];
108
+ if ( !isset( $series['metrics'] ) || !is_array( $series['data'] ) )
109
+ throw new \Exception( 'cant parse response - no metrics' );
110
+
111
+ $output = array();
112
+ foreach ( $series['data'] as $data ) {
113
+ $item = array();
114
+ for ( $m = 0; $m < count( $metrics ); $m++ )
115
+ $item[$metrics[$m]] = $data[$m];
116
+
117
+ $output[] = $item;
118
+ }
119
+
120
+ return $output;
121
+ }
122
+
123
+
124
+
125
+ public function service_cnames_get() {
126
+ $scope_id = $this->_get_scope_id();
127
+ $configuration = $this->api->configure_scope_get( $this->host_hash_code,
128
+ $scope_id );
129
+
130
+ $domains = array();
131
+
132
+ if ( isset( $configuration['hostname'] ) ) {
133
+ foreach ( $configuration['hostname'] as $d )
134
+ $domains[] = $d['domain'];
135
+ }
136
+
137
+ return $domains;
138
+ }
139
+
140
+
141
+
142
+ public function service_cnames_set( $domains ) {
143
+ $scope_id = $this->_get_scope_id();
144
+ $configuration = $this->api->configure_scope_get( $this->host_hash_code,
145
+ $scope_id );
146
+
147
+ $hostname = array();
148
+ foreach ( $domains as $d )
149
+ $hostname[] = array( 'domain' => $d );
150
+
151
+ $configuration['hostname'] = $hostname;
152
+ $this->api->configure_scope_set( $this->host_hash_code,
153
+ $scope_id, $configuration );
154
+ }
155
+
156
+
157
+
158
+ private function _get_scope_id() {
159
+ $scopes_response = $this->api->configure_scopes( $this->host_hash_code );
160
+ $scope_id = 0;
161
+
162
+ foreach ( $scopes_response['list'] as $scope ) {
163
+ if ( $scope['platform'] == 'CDS' )
164
+ return $scope['id'];
165
+ }
166
+
167
+ throw new Exception( 'scope CDN hasnt been created' );
168
+ }
169
+ }
CdnEngine_Mirror_MaxCdn.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN MaxCDN Class
6
+ */
7
+ class CdnEngine_Mirror_MaxCdn extends CdnEngine_Mirror_Netdna {}
CdnEngine_Mirror_Netdna.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 CDN Netdna Class
6
+ */
7
+
8
+ define( 'W3TC_CDN_NETDNA_URL', 'netdna-cdn.com' );
9
+
10
+ /**
11
+ * class CdnEngine_Mirror_Netdna
12
+ */
13
+ class CdnEngine_Mirror_Netdna extends CdnEngine_Mirror {
14
+ /**
15
+ * PHP5 Constructor
16
+ *
17
+ * @param array $config
18
+ */
19
+ function __construct( $config = array() ) {
20
+ $config = array_merge( array(
21
+ 'authorization_key' => '',
22
+ 'alias' => '',
23
+ 'consumerkey' => '',
24
+ 'consumersecret' => '',
25
+ 'zone_id' => 0
26
+ ), $config );
27
+ $split_keys = explode( '+', $config['authorization_key'] );
28
+ if ( sizeof( $split_keys )==3 )
29
+ list( $config['alias'], $config['consumerkey'], $config['consumersecret'] ) = $split_keys;
30
+ parent::__construct( $config );
31
+ }
32
+
33
+ /**
34
+ * Purges remote files
35
+ *
36
+ * @param array $files
37
+ * @param array $results
38
+ * @return boolean
39
+ */
40
+ function purge( $files, &$results ) {
41
+ if ( empty( $this->_config['authorization_key'] ) ) {
42
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) );
43
+
44
+ return false;
45
+ }
46
+
47
+ if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) {
48
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) );
49
+
50
+ return false;
51
+ }
52
+
53
+ if ( !class_exists( 'NetDNA' ) ) {
54
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
55
+ }
56
+
57
+ $api = new \NetDNA( $this->_config['alias'],
58
+ $this->_config['consumerkey'], $this->_config['consumersecret'] );
59
+ $results = array();
60
+
61
+ try {
62
+ if ( $this->_config['zone_id'] != 0 )
63
+ $zone_id = $this->_config['zone_id'];
64
+ else {
65
+ $zone_id = $api->get_zone_id( get_home_url() );
66
+ }
67
+
68
+ if ( $zone_id == 0 ) {
69
+ $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() );
70
+ }
71
+
72
+ if ( $zone_id == 0 ) {
73
+ $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) );
74
+ }
75
+
76
+ if ( $zone_id == 0 || is_null( $zone_id ) ) {
77
+ if ( Util_Environment::home_domain_root_url() == get_home_url() )
78
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) );
79
+ else
80
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) );
81
+ return !$this->_is_error( $results );
82
+ }
83
+
84
+
85
+ $files_to_pass = array();
86
+ foreach ( $files as $file )
87
+ $files_to_pass[] = '/' . $file['remote_path'];
88
+ $params = array( 'files' => $files_to_pass );
89
+ $file_purge = json_decode( $api->delete(
90
+ '/zones/pull.json/' . $zone_id . '/cache',
91
+ $params ) );
92
+
93
+ if ( preg_match( "(200|201)", $file_purge->code ) ) {
94
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' );
95
+ } else {
96
+ if ( preg_match( "(401|500)", $file_purge->code ) ) {
97
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) );
98
+ } else {
99
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code );
100
+ }
101
+ }
102
+ } catch ( W3tcWpHttpException $e ) {
103
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() );
104
+ }
105
+
106
+ return !$this->_is_error( $results );
107
+ }
108
+
109
+ /**
110
+ * Purge CDN completely
111
+ *
112
+ * @param unknown $results
113
+ * @return bool
114
+ */
115
+ function purge_all( &$results ) {
116
+ if ( empty( $this->_config['authorization_key'] ) ) {
117
+ $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Empty Authorization Key.', 'w3-total-cache' ) );
118
+
119
+ return false;
120
+ }
121
+
122
+ if ( empty( $this->_config['alias'] ) || empty( $this->_config['consumerkey'] ) || empty( $this->_config['consumersecret'] ) ) {
123
+ $results = $this->_get_results( array(), W3TC_CDN_RESULT_HALT, __( 'Malformed Authorization Key.', 'w3-total-cache' ) );
124
+
125
+ return false;
126
+ }
127
+
128
+ if ( !class_exists( 'NetDNA' ) ) {
129
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
130
+ }
131
+
132
+ $api = new \NetDNA( $this->_config['alias'], $this->_config['consumerkey'], $this->_config['consumersecret'] );
133
+
134
+ $results = array();
135
+
136
+ try {
137
+ if ( $this->_config['zone_id'] != 0 )
138
+ $zone_id = $this->_config['zone_id'];
139
+ else {
140
+ $zone_id = $api->get_zone_id( get_home_url() );
141
+ }
142
+
143
+ if ( $zone_id == 0 ) {
144
+ $zone_id = $api->get_zone_id( Util_Environment::home_domain_root_url() );
145
+ }
146
+
147
+
148
+ if ( $zone_id == 0 ) {
149
+ $zone_id = $api->get_zone_id( str_replace( '://', '://www.', Util_Environment::home_domain_root_url() ) );
150
+ }
151
+
152
+ if ( $zone_id == 0 || is_null( $zone_id ) ) {
153
+ if ( Util_Environment::home_domain_root_url() == get_home_url() )
154
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ) ) );
155
+ else
156
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'No zones match site: %s or %s.', 'w3-total-cache' ), trim( get_home_url(), '/' ), trim( Util_Environment::home_domain_root_url(), '/' ) ) );
157
+ return !$this->_is_error( $results );
158
+ }
159
+
160
+ $file_purge = json_decode( $api->delete( '/zones/pull.json/' . $zone_id . '/cache' ) );
161
+
162
+ if ( preg_match( "(200|201)", $file_purge->code ) ) {
163
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, __( 'OK', 'w3-total-cache' ) );
164
+ } else {
165
+ if ( preg_match( "(401|500)", $file_purge->code ) ) {
166
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, sprintf( __( 'Failed with error code %s. Please check your alias, consumer key, and private key.', 'w3-total-cache' ), $file_purge->code ) );
167
+ } else {
168
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_ERROR, __( 'Failed with error code ', 'w3-total-cache' ) . $file_purge->code );
169
+ }
170
+ }
171
+
172
+ } catch ( W3tcWpHttpException $e ) {
173
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT, __( 'Failure to pull zone: ', 'w3-total-cache' ) . $e->getMessage() );
174
+ }
175
+
176
+ return !$this->_is_error( $results );
177
+ }
178
+ }
CdnEngine_Mirror_RackSpaceCdn.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Rackspace CDN (pull) engine
6
+ */
7
+ class CdnEngine_Mirror_RackSpaceCdn extends CdnEngine_Mirror {
8
+ private $_access_state;
9
+ private $_service_id;
10
+ private $_domains;
11
+ private $_api;
12
+ private $_new_access_state_callback;
13
+
14
+
15
+
16
+ function __construct( $config = array() ) {
17
+ $config = array_merge( array(
18
+ 'user_name' => '',
19
+ 'api_key' => '',
20
+ 'region' => '',
21
+ 'service_id' => '',
22
+ 'service_access_url' => '',
23
+ 'service_protocol' => 'http',
24
+ 'domains' => array(),
25
+ 'access_state' => '',
26
+ 'new_access_state_callback' => ''
27
+ ), $config );
28
+
29
+ $this->_service_id = $config['service_id'];
30
+ $this->_new_access_state_callback = $config['new_access_state_callback'];
31
+
32
+ // init access state
33
+ $this->_access_state = @json_decode( $config['access_state'], true );
34
+ if ( !is_array( $this->_access_state ) )
35
+ $this->_access_state = array();
36
+ $this->_access_state = array_merge( array(
37
+ 'access_token' => '',
38
+ 'access_region_descriptor' => array()
39
+ ), $this->_access_state );
40
+
41
+ // cnames
42
+ if ( $config['service_protocol'] != 'https' && !empty( $config['domains'] ) )
43
+ $this->_domains = (array)$config['domains'];
44
+ else
45
+ $this->_domains = array( $config['service_access_url'] );
46
+
47
+ // form 'ssl' parameter based on service protocol
48
+ if ( $config['service_protocol'] == 'https' )
49
+ $config['ssl'] = 'enabled';
50
+ else
51
+ $config['ssl'] = 'disabled';
52
+
53
+ parent::__construct( $config );
54
+ $this->_create_api( array( $this, '_on_new_access_requested_api' ) );
55
+ }
56
+
57
+
58
+
59
+ private function _create_api( $new_access_required_callback_api ) {
60
+ $this->_api = new Cdn_RackSpace_Api_Cdn( array(
61
+ 'access_token' => $this->_access_state['access_token'],
62
+ 'access_region_descriptor' => $this->_access_state['access_region_descriptor'],
63
+ 'new_access_required' => $new_access_required_callback_api ) );
64
+ }
65
+
66
+
67
+
68
+ /**
69
+ * Called when new access token issued by api objects
70
+ */
71
+ public function _on_new_access_requested_api() {
72
+ $r = Cdn_RackSpace_Api_Tokens::authenticate( $this->_config['user_name'],
73
+ $this->_config['api_key'] );
74
+ if ( !isset( $r['access_token'] ) || !isset( $r['services'] ) )
75
+ throw new \Exception( 'Authentication failed' );
76
+ $r['regions'] = Cdn_RackSpace_Api_Tokens::cdn_services_by_region(
77
+ $r['services'] );
78
+
79
+ if ( !isset( $r['regions'][$this->_config['region']] ) )
80
+ throw new \Exception( 'Region ' . $this->_config['region'] . ' not found' );
81
+
82
+ $this->_access_state['access_token'] = $r['access_token'];
83
+ $this->_access_state['access_region_descriptor'] =
84
+ $r['regions'][$this->_config['region']];
85
+
86
+ $this->_create_api( array( $this, '_on_new_access_requested_second_time' ) );
87
+
88
+ if ( !empty( $this->_new_access_state_callback ) )
89
+ call_user_func( $this->_new_access_state_callback,
90
+ json_encode( $this->_access_state ) );
91
+
92
+ return $this->_api;
93
+ }
94
+
95
+
96
+
97
+ private function _on_new_access_requested_second_time() {
98
+ throw new \Exception( 'Authentication failed' );
99
+ }
100
+
101
+
102
+
103
+ /**
104
+ * Purges remote files
105
+ *
106
+ * @param array $files
107
+ * @param array $results
108
+ * @return boolean
109
+ */
110
+ function purge( $files, &$results ) {
111
+ $results = array();
112
+
113
+ try {
114
+ foreach ( $files as $file ) {
115
+ $url = $this->_format_url( $file['remote_path'] );
116
+ $this->_api->purge( $this->_service_id, $url );
117
+
118
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_OK, 'OK' );
119
+ }
120
+ } catch ( \Exception $e ) {
121
+ $results[] = $this->_get_result( '', '', W3TC_CDN_RESULT_HALT,
122
+ __( 'Failed to purge: ', 'w3-total-cache' ) . $e->getMessage() );
123
+ }
124
+
125
+ return !$this->_is_error( $results );
126
+ }
127
+
128
+
129
+
130
+ public function get_domains() {
131
+ return $this->_domains;
132
+ }
133
+
134
+
135
+
136
+ public function service_domains_get() {
137
+ $service = $this->_api->service_get( $this->_service_id );
138
+
139
+ $domains = array();
140
+
141
+ if ( isset( $service['domains'] ) ) {
142
+ foreach ( $service['domains'] as $d )
143
+ $domains[] = $d['domain'];
144
+ }
145
+
146
+ return $domains;
147
+ }
148
+
149
+
150
+
151
+ public function service_domains_set( $domains ) {
152
+ $value = array();
153
+ foreach ( $domains as $d ) {
154
+ $v = array( 'domain' => $d );
155
+ if ( $this->_config['service_protocol'] == 'https' )
156
+ $v['protocol'] = 'https';
157
+
158
+ $value[] = $v;
159
+ }
160
+
161
+ $this->_api->service_set( $this->_service_id,
162
+ array( array(
163
+ 'op' => 'replace',
164
+ 'path' => '/domains',
165
+ 'value' => $value ) ) );
166
+ }
167
+ }
CdnEngine_RackSpaceCloudFiles.php ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Rackspace Cloud Files CDN engine
6
+ */
7
+ class CdnEngine_RackSpaceCloudFiles extends CdnEngine_Base {
8
+ private $_access_state;
9
+ private $_container;
10
+ private $_api_files;
11
+ private $_api_cdn;
12
+
13
+
14
+
15
+ function __construct( $config = array() ) {
16
+ $config = array_merge( array(
17
+ 'user_name' => '',
18
+ 'api_key' => '',
19
+ 'region' => '',
20
+ 'container' => '',
21
+ 'cname' => array(),
22
+ 'access_state' => ''
23
+ ), $config );
24
+
25
+ $this->_container = $config['container'];
26
+ $this->_new_access_state_callback = $config['new_access_state_callback'];
27
+
28
+ // init access state
29
+ $this->_access_state = @json_decode( $config['access_state'], true );
30
+ if ( !is_array( $this->_access_state ) )
31
+ $this->_access_state = array();
32
+ $this->_access_state = array_merge( array(
33
+ 'access_token' => '',
34
+ 'access_region_descriptor' => array(),
35
+ 'host_http' => '',
36
+ 'host_https' => ''
37
+ ), $this->_access_state );
38
+
39
+ parent::__construct( $config );
40
+ $this->_create_api(
41
+ array( $this, '_on_new_access_requested_api_files' ),
42
+ array( $this, '_on_new_access_requested_api_cdn' ) );
43
+ }
44
+
45
+
46
+
47
+ private function _create_api( $new_access_required_callback_api_files,
48
+ $new_access_required_callback_api_cdn ) {
49
+ $this->_api_files = new Cdn_RackSpace_Api_CloudFiles( array(
50
+ 'access_token' => $this->_access_state['access_token'],
51
+ 'access_region_descriptor' => $this->_access_state['access_region_descriptor'],
52
+ 'new_access_required' => $new_access_required_callback_api_files ) );
53
+
54
+ $this->_api_cdn = new Cdn_RackSpace_Api_CloudFilesCdn( array(
55
+ 'access_token' => $this->_access_state['access_token'],
56
+ 'access_region_descriptor' => $this->_access_state['access_region_descriptor'],
57
+ 'new_access_required' => $new_access_required_callback_api_cdn ) );
58
+ }
59
+
60
+
61
+
62
+ /**
63
+ * Called when new access token issued by api objects
64
+ */
65
+ public function _on_new_access_requested_api_files() {
66
+ $this->_on_new_access_requested();
67
+ return $this->_api_files;
68
+ }
69
+
70
+
71
+
72
+ /**
73
+ * Called when new access token issued by api objects
74
+ */
75
+ public function _on_new_access_requested_api_cdn() {
76
+ $this->_on_new_access_requested();
77
+ return $this->_api_cdn;
78
+ }
79
+
80
+
81
+
82
+ private function _on_new_access_requested() {
83
+ $r = Cdn_RackSpace_Api_Tokens::authenticate( $this->_config['user_name'],
84
+ $this->_config['api_key'] );
85
+ if ( !isset( $r['access_token'] ) || !isset( $r['services'] ) )
86
+ throw new \Exception( 'Authentication failed' );
87
+ $r['regions'] = Cdn_RackSpace_Api_Tokens::cloudfiles_services_by_region(
88
+ $r['services'] );
89
+
90
+ if ( !isset( $r['regions'][$this->_config['region']] ) )
91
+ throw new \Exception( 'Region ' . $this->_config['region'] . ' not found' );
92
+
93
+ $this->_access_state['access_token'] = $r['access_token'];
94
+ $this->_access_state['access_region_descriptor'] =
95
+ $r['regions'][$this->_config['region']];
96
+
97
+ $this->_create_api(
98
+ array( $this, '_on_new_access_requested_second_time' ),
99
+ array( $this, '_on_new_access_requested_second_time' ) );
100
+
101
+ $c = $this->_api_cdn->container_get( $this->_config['container'] );
102
+
103
+ $this->_access_state['host_http'] = substr( $c['x-cdn-uri'], 7 );
104
+ $this->_access_state['host_https'] = substr( $c['x-cdn-ssl-uri'], 8 );
105
+
106
+ call_user_func( $this->_new_access_state_callback,
107
+ json_encode( $this->_access_state ) );
108
+ }
109
+
110
+
111
+
112
+ private function _on_new_access_requested_second_time() {
113
+ throw new \Exception( 'Authentication failed' );
114
+ }
115
+
116
+
117
+
118
+ /**
119
+ * Formats URL
120
+ */
121
+ function _format_url( $path ) {
122
+ $domain = $this->get_domain( $path );
123
+
124
+ if ( $domain ) {
125
+ $scheme = $this->_get_scheme();
126
+
127
+ // it does not support '+', requires '%2B'
128
+ $path = str_replace( '+', '%2B', $path );
129
+ $url = sprintf( '%s://%s/%s', $scheme, $domain, $path );
130
+
131
+ return $url;
132
+ }
133
+
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Uploads files to CDN
139
+ *
140
+ * @param array $files
141
+ * @param array $results
142
+ * @param boolean $force_rewrite
143
+ * @return boolean
144
+ */
145
+ function upload( $files, &$results, $force_rewrite = false,
146
+ $timeout_time = NULL ) {
147
+ foreach ( $files as $file ) {
148
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
149
+ break;
150
+
151
+ $local_path = $file['local_path'];
152
+ $remote_path = $file['remote_path'];
153
+
154
+ if ( !file_exists( $local_path ) ) {
155
+ $results[] = $this->_get_result( $local_path, $remote_path,
156
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
157
+
158
+ continue;
159
+ }
160
+
161
+ $file_content = file_get_contents( $local_path );
162
+
163
+ $do_write = true;
164
+
165
+ // rewrite is optional, check md5
166
+ if ( !$force_rewrite ) {
167
+ $object_meta = null;
168
+
169
+ try {
170
+ $object_meta = $this->_api_files->object_get_meta_or_null(
171
+ $this->_container, $remote_path );
172
+ } catch ( \Exception $exception ) {
173
+ $results[] = $this->_get_result( $local_path, $remote_path,
174
+ W3TC_CDN_RESULT_ERROR,
175
+ sprintf( 'Unable to check object (%s).', $exception->getMessage() ),
176
+ $file );
177
+ $do_write = false;
178
+ }
179
+
180
+ if ( is_array( $object_meta ) && isset( $object_meta['etag'] ) ) {
181
+ $md5_actual = md5( $file_content );
182
+
183
+ if ( $md5_actual == $object_meta['etag'] ) {
184
+ $results[] = $this->_get_result( $local_path,
185
+ $remote_path, W3TC_CDN_RESULT_OK,
186
+ 'Object up-to-date.', $file );
187
+ $do_write = false;
188
+ }
189
+ }
190
+ }
191
+
192
+ if ( $do_write ) {
193
+ try {
194
+ $this->_api_files->object_create( array(
195
+ 'container' => $this->_container,
196
+ 'name' => $remote_path,
197
+ 'content_type' => Util_Mime::get_mime_type( $local_path ),
198
+ 'content' => $file_content ) );
199
+ $results[] = $this->_get_result( $local_path, $remote_path,
200
+ W3TC_CDN_RESULT_OK, 'OK', $file );
201
+ } catch ( \Exception $exception ) {
202
+ $results[] = $this->_get_result( $local_path, $remote_path,
203
+ W3TC_CDN_RESULT_ERROR,
204
+ sprintf( 'Unable to create object (%s).', $exception->getMessage() ),
205
+ $file );
206
+ }
207
+ }
208
+ }
209
+
210
+ return !$this->_is_error( $results );
211
+ }
212
+
213
+ /**
214
+ * Deletes files from CDN
215
+ *
216
+ * @param array $files
217
+ * @param array $results
218
+ * @return boolean
219
+ */
220
+ function delete( $files, &$results ) {
221
+ foreach ( $files as $file ) {
222
+ $local_path = $file['local_path'];
223
+ $remote_path = $file['remote_path'];
224
+
225
+ try {
226
+ $this->_api_files->object_delete( $this->_container, $remote_path );
227
+ $results[] = $this->_get_result( $local_path, $remote_path,
228
+ W3TC_CDN_RESULT_OK, 'OK', $file );
229
+ } catch ( \Exception $exception ) {
230
+ $results[] = $this->_get_result( $local_path, $remote_path,
231
+ W3TC_CDN_RESULT_ERROR,
232
+ sprintf( 'Unable to delete object (%s).',
233
+ $exception->getMessage() ),
234
+ $file );
235
+ }
236
+ }
237
+
238
+ return !$this->_is_error( $results );
239
+ }
240
+
241
+ /**
242
+ * Test CDN connection
243
+ *
244
+ * @param string $error
245
+ * @return boolean
246
+ */
247
+ function test( &$error ) {
248
+ $filename = 'test_rscf_' . md5( time() );
249
+
250
+ try {
251
+ $object = $this->_api_files->object_create( array(
252
+ 'container' => $this->_container,
253
+ 'name' => $filename,
254
+ 'content_type' => 'text/plain',
255
+ 'content' => $filename ) );
256
+ } catch ( \Exception $exception ) {
257
+ $error = sprintf( 'Unable to write object (%s).', $exception->getMessage() );
258
+ return false;
259
+ }
260
+
261
+ $result = true;
262
+ try {
263
+ $r = wp_remote_get( 'http://' . $this->get_host_http() . '/' . $filename );
264
+
265
+ if ( $r['body'] != $filename ) {
266
+ $error = 'Failed to retrieve object after storing.';
267
+ $result = false;
268
+ }
269
+ } catch ( \Exception $exception ) {
270
+ $error = sprintf( 'Unable to read object (%s).', $exception->getMessage() );
271
+ $result = false;
272
+ }
273
+
274
+ try {
275
+ $this->_api_files->object_delete( $this->_container, $filename );
276
+ } catch ( \Exception $exception ) {
277
+ $error = sprintf( 'Unable to delete object (%s).', $exception->getMessage() );
278
+ $result = false;
279
+ }
280
+
281
+ return $result;
282
+ }
283
+
284
+ /**
285
+ * Returns CDN domain
286
+ *
287
+ * @return array
288
+ */
289
+ function get_domains() {
290
+ if ( Util_Environment::is_https() ) {
291
+ if ( !empty( $this->_config['cname'] ) ) {
292
+ return (array) $this->_config['cname'];
293
+ }
294
+
295
+ return array( $this->get_host_https() );
296
+ } else {
297
+ if ( !empty( $this->_config['cname'] ) ) {
298
+ return (array) $this->_config['cname'];
299
+ }
300
+
301
+ return array( $this->get_host_http() );
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Returns VIA string
307
+ *
308
+ * @return string
309
+ */
310
+ function get_via() {
311
+ return sprintf( 'Rackspace Cloud Files: %s', parent::get_via() );
312
+ }
313
+
314
+
315
+
316
+ public function get_host_http() {
317
+ if ( empty( $this->_access_state['host_http'] ) )
318
+ $this->_on_new_access_requested();
319
+
320
+ return $this->_access_state['host_http'];
321
+ }
322
+
323
+
324
+
325
+ public function get_host_https() {
326
+ if ( empty( $this->_access_state['host_https'] ) )
327
+ $this->_on_new_access_requested();
328
+
329
+ return $this->_access_state['host_https'];
330
+ }
331
+ }
CdnEngine_S3.php ADDED
@@ -0,0 +1,466 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Amazon S3 CDN engine
6
+ */
7
+
8
+ if ( !class_exists( 'S3' ) ) {
9
+ require_once W3TC_LIB_DIR . '/S3.php';
10
+ }
11
+
12
+ /**
13
+ * class CdnEngine_S3
14
+ */
15
+ class CdnEngine_S3 extends CdnEngine_Base {
16
+ /**
17
+ * S3 object
18
+ *
19
+ * @var S3
20
+ */
21
+ var $_s3 = null;
22
+
23
+ /**
24
+ * PHP5 Constructor
25
+ *
26
+ * @param array $config
27
+ */
28
+ function __construct( $config = array() ) {
29
+ $config = array_merge( array(
30
+ 'key' => '',
31
+ 'secret' => '',
32
+ 'bucket' => '',
33
+ 'cname' => array(),
34
+ ), $config );
35
+
36
+ parent::__construct( $config );
37
+ }
38
+
39
+ /**
40
+ * Formats URL
41
+ *
42
+ * @param string $path
43
+ * @return string
44
+ */
45
+ function _format_url( $path ) {
46
+ $domain = $this->get_domain( $path );
47
+
48
+ if ( $domain ) {
49
+ $scheme = $this->_get_scheme();
50
+
51
+ // it does not support '+', requires '%2B'
52
+ $path = str_replace( '+', '%2B', $path );
53
+ $url = sprintf( '%s://%s/%s', $scheme, $domain, $path );
54
+
55
+ return $url;
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ /**
62
+ * Inits S3 object
63
+ *
64
+ * @param string $error
65
+ * @return boolean
66
+ */
67
+ function _init( &$error ) {
68
+ if ( empty( $this->_config['key'] ) ) {
69
+ $error = 'Empty access key.';
70
+
71
+ return false;
72
+ }
73
+
74
+ if ( empty( $this->_config['secret'] ) ) {
75
+ $error = 'Empty secret key.';
76
+
77
+ return false;
78
+ }
79
+
80
+ if ( empty( $this->_config['bucket'] ) ) {
81
+ $error = 'Empty bucket.';
82
+
83
+ return false;
84
+ }
85
+
86
+ $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], false );
87
+
88
+ return true;
89
+ }
90
+
91
+ /**
92
+ * Uploads files to S3
93
+ *
94
+ * @param array $files
95
+ * @param array $results
96
+ * @param boolean $force_rewrite
97
+ * @return boolean
98
+ */
99
+ function upload( $files, &$results, $force_rewrite = false,
100
+ $timeout_time = NULL ) {
101
+ $error = null;
102
+
103
+ if ( !$this->_init( $error ) ) {
104
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
105
+
106
+ return false;
107
+ }
108
+
109
+ foreach ( $files as $file ) {
110
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
111
+ break;
112
+
113
+ $local_path = $file['local_path'];
114
+ $remote_path = $file['remote_path'];
115
+
116
+ $results[] = $this->_upload( $file, $force_rewrite );
117
+
118
+ if ( $this->_config['compression'] && $this->_may_gzip( $remote_path ) ) {
119
+ $file['remote_path_gzip'] = $remote_path . $this->_gzip_extension;
120
+ $results[] = $this->_upload_gzip( $file, $force_rewrite );
121
+ }
122
+ }
123
+
124
+ return !$this->_is_error( $results );
125
+ }
126
+
127
+ /**
128
+ * Uploads single file to S3
129
+ *
130
+ * @param array CDN file array
131
+ * @param boolean $force_rewrite
132
+ * @return array
133
+ */
134
+ function _upload( $file, $force_rewrite = false ) {
135
+ $local_path = $file['local_path'];
136
+ $remote_path = $file['remote_path'];
137
+
138
+ if ( !file_exists( $local_path ) ) {
139
+ return $this->_get_result( $local_path, $remote_path,
140
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
141
+ }
142
+
143
+ if ( !$force_rewrite ) {
144
+ $this->_set_error_handler();
145
+ $info = @$this->_s3->getObjectInfo( $this->_config['bucket'], $remote_path );
146
+ $this->_restore_error_handler();
147
+
148
+ if ( $info ) {
149
+ $hash = @md5_file( $local_path );
150
+ $s3_hash = ( isset( $info['hash'] ) ? $info['hash'] : '' );
151
+
152
+ if ( $hash === $s3_hash ) {
153
+ return $this->_get_result( $local_path, $remote_path,
154
+ W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
155
+ }
156
+ }
157
+ }
158
+
159
+ $headers = $this->_get_headers( $file );
160
+
161
+ $this->_set_error_handler();
162
+ $result = @$this->_s3->putObjectFile( $local_path, $this->_config['bucket'], $remote_path, \S3::ACL_PUBLIC_READ, array(), $headers );
163
+ $this->_restore_error_handler();
164
+
165
+ if ( $result ) {
166
+ return $this->_get_result( $local_path, $remote_path,
167
+ W3TC_CDN_RESULT_OK, 'OK', $file );
168
+ }
169
+
170
+ return $this->_get_result( $local_path, $remote_path,
171
+ W3TC_CDN_RESULT_ERROR,
172
+ sprintf( 'Unable to put object (%s).', $this->_get_last_error() ),
173
+ $file );
174
+ }
175
+
176
+ /**
177
+ * Uploads gzip version of file
178
+ *
179
+ * @param string $local_path
180
+ * @param string $remote_path
181
+ * @param boolean $force_rewrite
182
+ * @return array
183
+ */
184
+ function _upload_gzip( $file, $force_rewrite = false ) {
185
+ $local_path = $file['local_path'];
186
+ $remote_path = $file['remote_path_gzip'];
187
+
188
+ if ( !function_exists( 'gzencode' ) ) {
189
+ return $this->_get_result( $local_path, $remote_path,
190
+ W3TC_CDN_RESULT_ERROR, "GZIP library doesn't exist.", $file );
191
+ }
192
+
193
+ if ( !file_exists( $local_path ) ) {
194
+ return $this->_get_result( $local_path, $remote_path,
195
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
196
+ }
197
+
198
+ $contents = @file_get_contents( $local_path );
199
+
200
+ if ( $contents === false ) {
201
+ return $this->_get_result( $local_path, $remote_path,
202
+ W3TC_CDN_RESULT_ERROR, 'Unable to read file.', $file );
203
+ }
204
+
205
+ $data = gzencode( $contents );
206
+
207
+ if ( !$force_rewrite ) {
208
+ $this->_set_error_handler();
209
+ $info = @$this->_s3->getObjectInfo( $this->_config['bucket'], $remote_path );
210
+ $this->_restore_error_handler();
211
+
212
+ if ( $info ) {
213
+ $hash = md5( $data );
214
+ $s3_hash = ( isset( $info['hash'] ) ? $info['hash'] : '' );
215
+
216
+ if ( $hash === $s3_hash ) {
217
+ return $this->_get_result( $local_path, $remote_path,
218
+ W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
219
+ }
220
+ }
221
+ }
222
+
223
+ $headers = $this->_get_headers( $file );
224
+ $headers = array_merge( $headers, array(
225
+ 'Vary' => 'Accept-Encoding',
226
+ 'Content-Encoding' => 'gzip'
227
+ ) );
228
+
229
+ $this->_set_error_handler();
230
+ $result = @$this->_s3->putObjectString( $data, $this->_config['bucket'], $remote_path, \S3::ACL_PUBLIC_READ, array(), $headers );
231
+ $this->_restore_error_handler();
232
+
233
+ if ( $result ) {
234
+ return $this->_get_result( $local_path, $remote_path,
235
+ W3TC_CDN_RESULT_OK, 'OK', $file );
236
+ }
237
+
238
+ return $this->_get_result( $local_path, $remote_path,
239
+ W3TC_CDN_RESULT_ERROR,
240
+ sprintf( 'Unable to put object (%s).', $this->_get_last_error() ),
241
+ $file );
242
+ }
243
+
244
+ /**
245
+ * Deletes files from S3
246
+ *
247
+ * @param array $files
248
+ * @param array $results
249
+ * @return boolean
250
+ */
251
+ function delete( $files, &$results ) {
252
+ $error = null;
253
+
254
+ if ( !$this->_init( $error ) ) {
255
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
256
+
257
+ return false;
258
+ }
259
+
260
+ foreach ( $files as $file ) {
261
+ $local_path = $file['local_path'];
262
+ $remote_path = $file['remote_path'];
263
+
264
+ $this->_set_error_handler();
265
+ $result = @$this->_s3->deleteObject( $this->_config['bucket'], $remote_path );
266
+ $this->_restore_error_handler();
267
+
268
+ if ( $result ) {
269
+ $results[] = $this->_get_result( $local_path, $remote_path,
270
+ W3TC_CDN_RESULT_OK, 'OK', $file );
271
+ } else {
272
+ $results[] = $this->_get_result( $local_path, $remote_path,
273
+ W3TC_CDN_RESULT_ERROR,
274
+ sprintf( 'Unable to delete object (%s).',
275
+ $this->_get_last_error() ),
276
+ $file );
277
+ }
278
+
279
+ if ( $this->_config['compression'] ) {
280
+ $remote_path_gzip = $remote_path . $this->_gzip_extension;
281
+
282
+ $this->_set_error_handler();
283
+ $result = @$this->_s3->deleteObject( $this->_config['bucket'], $remote_path_gzip );
284
+ $this->_restore_error_handler();
285
+
286
+ if ( $result ) {
287
+ $results[] = $this->_get_result( $local_path,
288
+ $remote_path_gzip, W3TC_CDN_RESULT_OK, 'OK', $file );
289
+ } else {
290
+ $results[] = $this->_get_result( $local_path,
291
+ $remote_path_gzip, W3TC_CDN_RESULT_ERROR,
292
+ sprintf( 'Unable to delete object (%s).',
293
+ $this->_get_last_error() ),
294
+ $file );
295
+ }
296
+ }
297
+ }
298
+
299
+ return !$this->_is_error( $results );
300
+ }
301
+
302
+ /**
303
+ * Tests S3
304
+ *
305
+ * @param string $error
306
+ * @return boolean
307
+ */
308
+ function test( &$error ) {
309
+ if ( !parent::test( $error ) ) {
310
+ return false;
311
+ }
312
+
313
+ $string = 'test_s3_' . md5( time() );
314
+
315
+ if ( !$this->_init( $error ) ) {
316
+ return false;
317
+ }
318
+
319
+ $this->_set_error_handler();
320
+
321
+ $buckets = @$this->_s3->listBuckets();
322
+
323
+ if ( $buckets === false ) {
324
+ $error = sprintf( 'Unable to list buckets (%s).', $this->_get_last_error() );
325
+
326
+ $this->_restore_error_handler();
327
+
328
+ return false;
329
+ }
330
+
331
+ if ( !in_array( $this->_config['bucket'], (array) $buckets ) ) {
332
+ $error = sprintf( 'Bucket doesn\'t exist: %s.', $this->_config['bucket'] );
333
+
334
+ $this->_restore_error_handler();
335
+
336
+ return false;
337
+ }
338
+
339
+ if ( !@$this->_s3->putObjectString( $string, $this->_config['bucket'], $string, \S3::ACL_PUBLIC_READ ) ) {
340
+ $error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() );
341
+
342
+ $this->_restore_error_handler();
343
+
344
+ return false;
345
+ }
346
+
347
+ if ( !( $object = @$this->_s3->getObject( $this->_config['bucket'], $string ) ) ) {
348
+ $error = sprintf( 'Unable to get object (%s).', $this->_get_last_error() );
349
+
350
+ $this->_restore_error_handler();
351
+
352
+ return false;
353
+ }
354
+
355
+ if ( $object->body != $string ) {
356
+ $error = 'Objects are not equal.';
357
+
358
+ @$this->_s3->deleteObject( $this->_config['bucket'], $string );
359
+ $this->_restore_error_handler();
360
+
361
+ return false;
362
+ }
363
+
364
+ if ( !@$this->_s3->deleteObject( $this->_config['bucket'], $string ) ) {
365
+ $error = sprintf( 'Unable to delete object (%s).', $this->_get_last_error() );
366
+
367
+ $this->_restore_error_handler();
368
+
369
+ return false;
370
+ }
371
+
372
+ $this->_restore_error_handler();
373
+
374
+ return true;
375
+ }
376
+
377
+ /**
378
+ * Returns CDN domain
379
+ *
380
+ * @return array
381
+ */
382
+ function get_domains() {
383
+ if ( !empty( $this->_config['cname'] ) ) {
384
+ return (array) $this->_config['cname'];
385
+ } elseif ( !empty( $this->_config['bucket'] ) ) {
386
+ $domain = sprintf( '%s.s3.amazonaws.com', $this->_config['bucket'] );
387
+
388
+ return array(
389
+ $domain
390
+ );
391
+ }
392
+
393
+ return array();
394
+ }
395
+
396
+ /**
397
+ * Returns via string
398
+ *
399
+ * @return string
400
+ */
401
+ function get_via() {
402
+ return sprintf( 'Amazon Web Services: S3: %s', parent::get_via() );
403
+ }
404
+
405
+ /**
406
+ * Creates bucket
407
+ *
408
+ * @param string $container_id
409
+ * @param string $error
410
+ * @return boolean
411
+ */
412
+ function create_container( &$container_id, &$error ) {
413
+ if ( !$this->_init( $error ) ) {
414
+ return false;
415
+ }
416
+
417
+ $this->_set_error_handler();
418
+
419
+ $buckets = @$this->_s3->listBuckets();
420
+
421
+ if ( $buckets === false ) {
422
+ $error = sprintf( 'Unable to list buckets (%s).', $this->_get_last_error() );
423
+
424
+ $this->_restore_error_handler();
425
+
426
+ return false;
427
+ }
428
+
429
+ if ( in_array( $this->_config['bucket'], (array) $buckets ) ) {
430
+ $error = sprintf( 'Bucket already exists: %s.', $this->_config['bucket'] );
431
+
432
+ $this->_restore_error_handler();
433
+
434
+ return false;
435
+ }
436
+
437
+ if ( empty( $this->_config['bucket_acl'] ) ) {
438
+ $this->_config['bucket_acl'] = \S3::ACL_PRIVATE;
439
+ }
440
+
441
+ if ( !isset( $this->_config['bucket_location'] ) ) {
442
+ $this->_config['bucket_location'] = \S3::LOCATION_US;
443
+ }
444
+
445
+ if ( !@$this->_s3->putBucket( $this->_config['bucket'], $this->_config['bucket_acl'], $this->_config['bucket_location'] ) ) {
446
+ $error = sprintf( 'Unable to create bucket: %s (%s).', $this->_config['bucket'], $this->_get_last_error() );
447
+
448
+ $this->_restore_error_handler();
449
+
450
+ return false;
451
+ }
452
+
453
+ $this->_restore_error_handler();
454
+
455
+ return true;
456
+ }
457
+
458
+ /**
459
+ * How and if headers should be set
460
+ *
461
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
462
+ */
463
+ function headers_support() {
464
+ return W3TC_CDN_HEADER_UPLOADABLE;
465
+ }
466
+ }
CdnEngine_S3_Cf.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Amazon CloudFront CDN engine
6
+ */
7
+
8
+ define( 'W3TC_CDN_CF_TYPE_S3', 's3' );
9
+ define( 'W3TC_CDN_CF_TYPE_CUSTOM', 'custom' );
10
+
11
+ /**
12
+ * class CdnEngine_S3_Cf
13
+ */
14
+ class CdnEngine_S3_Cf extends CdnEngine_S3 {
15
+ /**
16
+ * Type
17
+ *
18
+ * @var string
19
+ */
20
+ var $type = '';
21
+
22
+ /**
23
+ * PHP5 Constructor
24
+ *
25
+ * @param array $config
26
+ */
27
+ function __construct( $config = array() ) {
28
+ $config = array_merge( array(
29
+ 'id' => ''
30
+ ), $config );
31
+
32
+ parent::__construct( $config );
33
+ }
34
+
35
+ /**
36
+ * Initializes S3 object
37
+ *
38
+ * @param string $error
39
+ * @return bool
40
+ */
41
+ function _init( &$error ) {
42
+ if ( empty( $this->type ) ) {
43
+ $error = 'Empty type.';
44
+
45
+ return false;
46
+ } elseif ( !in_array( $this->type, array( W3TC_CDN_CF_TYPE_S3, W3TC_CDN_CF_TYPE_CUSTOM ) ) ) {
47
+ $error = 'Invalid type.';
48
+
49
+ return false;
50
+ }
51
+
52
+ if ( empty( $this->_config['key'] ) ) {
53
+ $error = 'Empty access key.';
54
+
55
+ return false;
56
+ }
57
+
58
+ if ( empty( $this->_config['secret'] ) ) {
59
+ $error = 'Empty secret key.';
60
+
61
+ return false;
62
+ }
63
+
64
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 && empty( $this->_config['bucket'] ) ) {
65
+ $error = 'Empty bucket.';
66
+
67
+ return false;
68
+ }
69
+
70
+ $this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'], false );
71
+
72
+ return true;
73
+ }
74
+
75
+ /**
76
+ * Returns origin
77
+ *
78
+ * @return string
79
+ */
80
+ function _get_origin() {
81
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
82
+ $origin = sprintf( '%s.s3.amazonaws.com', $this->_config['bucket'] );
83
+ } else {
84
+ $origin = Util_Environment::host_port();
85
+ }
86
+
87
+ return $origin;
88
+ }
89
+
90
+ /**
91
+ * Upload files
92
+ *
93
+ * @param array $files
94
+ * @param array $results
95
+ * @param boolean $force_rewrite
96
+ * @return boolean
97
+ */
98
+ function upload( $files, &$results, $force_rewrite = false,
99
+ $timeout_time = NULL ) {
100
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
101
+ return parent::upload( $files, $results, $force_rewrite,
102
+ $timeout_time );
103
+ } else {
104
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
105
+
106
+ return true;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Delete files from CDN
112
+ *
113
+ * @param array $files
114
+ * @param array $results
115
+ * @return boolean
116
+ */
117
+ function delete( $files, &$results ) {
118
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
119
+ return parent::delete( $files, $results );
120
+ } else {
121
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
122
+
123
+ return true;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Purge files from CDN
129
+ *
130
+ * @param array $files
131
+ * @param array $results
132
+ * @return boolean
133
+ */
134
+ function purge( $files, &$results ) {
135
+ if ( parent::purge( $files, $results ) ) {
136
+ return $this->invalidate( $files, $results );
137
+ }
138
+
139
+ return false;
140
+ }
141
+
142
+ /**
143
+ * Invalidates files
144
+ *
145
+ * @param array $files
146
+ * @param array $results
147
+ * @return boolean
148
+ */
149
+ function invalidate( $files, &$results ) {
150
+ if ( !$this->_init( $error ) ) {
151
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
152
+
153
+ return false;
154
+ }
155
+
156
+ $this->_set_error_handler();
157
+ $dists = @$this->_s3->listDistributions();
158
+ $this->_restore_error_handler();
159
+
160
+ if ( $dists === false ) {
161
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() ) );
162
+
163
+ return false;
164
+ }
165
+
166
+ if ( !count( $dists ) ) {
167
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, 'No distributions found.' );
168
+
169
+ return false;
170
+ }
171
+
172
+ $dist = false;
173
+ $origin = $this->_get_origin();
174
+
175
+ foreach ( (array) $dists as $_dist ) {
176
+ if ( isset( $_dist['origin'] ) && $_dist['origin'] == $origin ) {
177
+ $dist = $_dist;
178
+ break;
179
+ }
180
+ }
181
+
182
+ if ( !$dist ) {
183
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Distribution for origin "%s" not found.', $origin ) );
184
+
185
+ return false;
186
+ }
187
+
188
+ $paths = array();
189
+
190
+ foreach ( $files as $file ) {
191
+ $remote_file = $file['remote_path'];
192
+ $paths[] = '/' . $remote_file;
193
+ }
194
+
195
+ $this->_set_error_handler();
196
+ $invalidation = @$this->_s3->createInvalidation( $dist['id'], $paths );
197
+ $this->_restore_error_handler();
198
+
199
+ if ( !$invalidation ) {
200
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Unable to create invalidation bath (%s).', $this->_get_last_error() ) );
201
+
202
+ return false;
203
+ }
204
+
205
+ $results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
206
+
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Returns array of CDN domains
212
+ *
213
+ * @return array
214
+ */
215
+ function get_domains() {
216
+ if ( !empty( $this->_config['cname'] ) ) {
217
+ return (array) $this->_config['cname'];
218
+ } elseif ( !empty( $this->_config['id'] ) ) {
219
+ $domain = sprintf( '%s.cloudfront.net', $this->_config['id'] );
220
+
221
+ return array(
222
+ $domain
223
+ );
224
+ }
225
+
226
+ return array();
227
+ }
228
+
229
+ /**
230
+ * Tests CF
231
+ *
232
+ * @param string $error
233
+ * @return boolean
234
+ */
235
+ function test( &$error ) {
236
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
237
+ if ( !parent::test( $error ) ) {
238
+ return false;
239
+ }
240
+ } elseif ( $this->type == W3TC_CDN_CF_TYPE_CUSTOM ) {
241
+ if ( !$this->_init( $error ) ) {
242
+ return false;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Search active CF distribution
248
+ */
249
+ $this->_set_error_handler();
250
+ $dists = @$this->_s3->listDistributions();
251
+ $this->_restore_error_handler();
252
+
253
+ if ( $dists === false ) {
254
+ $error = sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() );
255
+
256
+ return false;
257
+ }
258
+
259
+ if ( !count( $dists ) ) {
260
+ $error = 'No distributions found.';
261
+
262
+ return false;
263
+ }
264
+
265
+ $dist = false;
266
+ $origin = $this->_get_origin();
267
+
268
+ foreach ( (array) $dists as $_dist ) {
269
+ if ( isset( $_dist['origin'] ) && $_dist['origin'] == $origin ) {
270
+ $dist = $_dist;
271
+ break;
272
+ }
273
+ }
274
+
275
+ if ( !$dist ) {
276
+ $error = sprintf( 'Distribution for origin "%s" not found.', $origin );
277
+
278
+ return false;
279
+ }
280
+
281
+ if ( !$dist['enabled'] ) {
282
+ $error = sprintf( 'Distribution for origin "%s" is disabled.', $origin );
283
+
284
+ return false;
285
+ }
286
+
287
+ if ( !empty( $this->_config['cname'] ) ) {
288
+ $domains = (array) $this->_config['cname'];
289
+ $cnames = ( isset( $dist['cnames'] ) ? (array) $dist['cnames'] : array() );
290
+
291
+ foreach ( $domains as $domain ) {
292
+ $_domains = array_map( 'trim', explode( ',', $domain ) );
293
+
294
+ foreach ( $_domains as $_domain ) {
295
+ if ( !in_array( $_domain, $cnames ) ) {
296
+ $error = sprintf( 'Domain name %s is not in distribution CNAME list.', $_domain );
297
+
298
+ return false;
299
+ }
300
+ }
301
+ }
302
+ } elseif ( !empty( $this->_config['id'] ) ) {
303
+ $domain = $this->get_domain();
304
+
305
+ if ( $domain != $dist['domain'] ) {
306
+ $error = sprintf( 'Distribution domain name mismatch (%s != %s).', $domain, $dist['domain'] );
307
+
308
+ return false;
309
+ }
310
+ }
311
+
312
+ return true;
313
+ }
314
+
315
+ /**
316
+ * Create bucket
317
+ *
318
+ * @param string $container_id
319
+ * @param string $error
320
+ * @return boolean
321
+ */
322
+ function create_container( &$container_id, &$error ) {
323
+ if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
324
+ if ( !parent::create_container( $container_id, $error ) ) {
325
+ return false;
326
+ }
327
+ } elseif ( $this->type == W3TC_CDN_CF_TYPE_CUSTOM ) {
328
+ if ( !$this->_init( $error ) ) {
329
+ return false;
330
+ }
331
+ }
332
+
333
+ $cnames = array();
334
+
335
+ if ( !empty( $this->_config['cname'] ) ) {
336
+ $domains = (array) $this->_config['cname'];
337
+
338
+ foreach ( $domains as $domain ) {
339
+ $_domains = array_map( 'trim', explode( ',', $domain ) );
340
+
341
+ foreach ( $_domains as $_domain ) {
342
+ $cnames[] = $_domain;
343
+ }
344
+ }
345
+ }
346
+
347
+ $origin = $this->_get_origin();
348
+
349
+ $this->_set_error_handler();
350
+ $dist = @$this->_s3->createDistribution( $origin, $this->type, true, $cnames );
351
+ $this->_restore_error_handler();
352
+
353
+ if ( !$dist ) {
354
+ $error = sprintf( 'Unable to create distribution for origin %s (%s).', $origin, $this->_get_last_error() );
355
+
356
+ return false;
357
+ }
358
+
359
+ $matches = null;
360
+
361
+ if ( preg_match( '~^(.+)\.cloudfront\.net$~', $dist['domain'], $matches ) ) {
362
+ $container_id = $matches[1];
363
+ }
364
+
365
+ return true;
366
+ }
367
+
368
+ /**
369
+ * Returns via string
370
+ *
371
+ * @return string
372
+ */
373
+ function get_via() {
374
+ $domain = $this->get_domain();
375
+
376
+ $via = ( $domain ? $domain : 'N/A' );
377
+
378
+ return sprintf( 'Amazon Web Services: CloudFront: %s', $via );
379
+ }
380
+
381
+ /**
382
+ * Update distribution CNAMEs
383
+ *
384
+ * @param string $error
385
+ * @return boolean
386
+ */
387
+ function update_cnames( &$error ) {
388
+ if ( !$this->_init( $error ) ) {
389
+ return false;
390
+ }
391
+
392
+ $this->_set_error_handler();
393
+ $dists = @$this->_s3->listDistributions();
394
+ $this->_restore_error_handler();
395
+
396
+ if ( $dists === false ) {
397
+ $error = sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() );
398
+
399
+ return false;
400
+ }
401
+
402
+ $dist_id = false;
403
+ $origin = $this->_get_origin();
404
+
405
+ foreach ( (array) $dists as $dist ) {
406
+ if ( isset( $dist['origin'] ) && $dist['origin'] == $origin ) {
407
+ $dist_id = $dist['id'];
408
+ break;
409
+ }
410
+ }
411
+
412
+ if ( !$dist_id ) {
413
+ $error = sprintf( 'Distribution ID for origin "%s" not found.', $origin );
414
+
415
+ return false;
416
+ }
417
+
418
+ $this->_set_error_handler();
419
+ $dist = @$this->_s3->getDistribution( $dist_id );
420
+ $this->_restore_error_handler();
421
+
422
+ if ( !$dist ) {
423
+ $error = sprintf( 'Unable to get distribution by ID: %s (%s).', $dist_id, $this->_get_last_error() );
424
+ }
425
+
426
+ $dist['cnames'] = ( isset( $this->_config['cname'] ) ? (array) $this->_config['cname'] : array() );
427
+
428
+ $this->_set_error_handler();
429
+ $dist = @$this->_s3->updateDistribution( $dist );
430
+ $this->_restore_error_handler();
431
+
432
+ if ( !$dist ) {
433
+ $error = sprintf( 'Unable to update distribution: %s (%s).', json_encode( $dist ), $this->_get_last_error() );
434
+
435
+ return false;
436
+ }
437
+
438
+ return true;
439
+ }
440
+ }
CdnEngine_S3_Cf_Custom.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Amazon CloudFront (Custom origin) CDN engine
6
+ */
7
+
8
+ class CdnEngine_S3_Cf_Custom extends CdnEngine_S3_Cf {
9
+ var $type = W3TC_CDN_CF_TYPE_CUSTOM;
10
+
11
+ /**
12
+ * How and if headers should be set
13
+ *
14
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
15
+ */
16
+ function headers_support() {
17
+ return W3TC_CDN_HEADER_MIRRORING;
18
+ }
19
+ }
CdnEngine_S3_Cf_S3.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Amazon CloudFront (S3 origin) CDN engine
6
+ */
7
+
8
+ class CdnEngine_S3_Cf_S3 extends CdnEngine_S3_Cf {
9
+ var $type = W3TC_CDN_CF_TYPE_S3;
10
+ }
CdnEngine_S3_Compatible.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Amazon S3 CDN engine
6
+ */
7
+
8
+ if ( !class_exists( 'S3' ) ) {
9
+ require_once W3TC_LIB_DIR . '/S3.php';
10
+ }
11
+
12
+ /**
13
+ * class CdnEngine_S3
14
+ */
15
+ class CdnEngine_S3_Compatible extends CdnEngine_Base {
16
+ /**
17
+ * S3 object
18
+ */
19
+ private $_s3 = null;
20
+
21
+ /**
22
+ * PHP5 Constructor
23
+ *
24
+ * @param array $config
25
+ */
26
+ function __construct( $config = array() ) {
27
+ $config = array_merge( array(
28
+ 'key' => '',
29
+ 'secret' => '',
30
+ 'bucket' => '',
31
+ 'cname' => array(),
32
+ ), $config );
33
+
34
+ $this->_s3 = new \S3( $config['key'], $config['secret'], false,
35
+ $config['api_host'] );
36
+ parent::__construct( $config );
37
+ }
38
+
39
+ /**
40
+ * Formats URL
41
+ *
42
+ * @param string $path
43
+ * @return string
44
+ */
45
+ function _format_url( $path ) {
46
+ $domain = $this->get_domain( $path );
47
+
48
+ if ( $domain ) {
49
+ $scheme = $this->_get_scheme();
50
+
51
+ // it does not support '+', requires '%2B'
52
+ $path = str_replace( '+', '%2B', $path );
53
+ $url = sprintf( '%s://%s/%s', $scheme, $domain, $path );
54
+
55
+ return $url;
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ /**
62
+ * Uploads files to S3
63
+ *
64
+ * @param array $files
65
+ * @param array $results
66
+ * @param boolean $force_rewrite
67
+ * @return boolean
68
+ */
69
+ function upload( $files, &$results, $force_rewrite = false,
70
+ $timeout_time = NULL ) {
71
+
72
+ $error = null;
73
+
74
+ foreach ( $files as $file ) {
75
+ if ( !is_null( $timeout_time ) && time() > $timeout_time )
76
+ break;
77
+
78
+ $local_path = $file['local_path'];
79
+ $remote_path = $file['remote_path'];
80
+
81
+ $results[] = $this->_upload( $file, $force_rewrite );
82
+
83
+ if ( $this->_config['compression'] && $this->_may_gzip( $remote_path ) ) {
84
+ $file['remote_path_gzip'] = $remote_path . $this->_gzip_extension;
85
+ $results[] = $this->_upload_gzip( $file, $force_rewrite );
86
+ }
87
+ }
88
+
89
+ return !$this->_is_error( $results );
90
+ }
91
+
92
+ /**
93
+ * Uploads single file to S3
94
+ *
95
+ * @param array CDN file array
96
+ * @param boolean $force_rewrite
97
+ * @return array
98
+ */
99
+ function _upload( $file, $force_rewrite = false ) {
100
+ $local_path = $file['local_path'];
101
+ $remote_path = $file['remote_path'];
102
+
103
+ if ( !file_exists( $local_path ) ) {
104
+ return $this->_get_result( $local_path, $remote_path,
105
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
106
+ }
107
+
108
+ if ( !$force_rewrite ) {
109
+ $this->_set_error_handler();
110
+ $info = @$this->_s3->getObjectInfo( $this->_config['bucket'],
111
+ $remote_path );
112
+ $this->_restore_error_handler();
113
+
114
+ if ( $info ) {
115
+ $hash = @md5_file( $local_path );
116
+ $s3_hash = ( isset( $info['hash'] ) ? $info['hash'] : '' );
117
+
118
+ if ( $hash === $s3_hash ) {
119
+ return $this->_get_result( $local_path, $remote_path,
120
+ W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
121
+ }
122
+ }
123
+ }
124
+
125
+ $headers = $this->_get_headers( $file );
126
+
127
+ $this->_set_error_handler();
128
+ $result = @$this->_s3->putObjectFile( $local_path,
129
+ $this->_config['bucket'], $remote_path,
130
+ \S3::ACL_PUBLIC_READ, array(), $headers );
131
+ $this->_restore_error_handler();
132
+
133
+ if ( $result ) {
134
+ return $this->_get_result( $local_path, $remote_path,
135
+ W3TC_CDN_RESULT_OK, 'OK', $file );
136
+ }
137
+
138
+ return $this->_get_result( $local_path, $remote_path,
139
+ W3TC_CDN_RESULT_ERROR,
140
+ sprintf( 'Unable to put object (%s).', $this->_get_last_error() ),
141
+ $file );
142
+ }
143
+
144
+ /**
145
+ * Uploads gzip version of file
146
+ *
147
+ * @param string $local_path
148
+ * @param string $remote_path
149
+ * @param boolean $force_rewrite
150
+ * @return array
151
+ */
152
+ function _upload_gzip( $file, $force_rewrite = false ) {
153
+ $local_path = $file['local_path'];
154
+ $remote_path = $file['remote_path_gzip'];
155
+
156
+ if ( !function_exists( 'gzencode' ) )
157
+ return $this->_get_result( $local_path, $remote_path,
158
+ W3TC_CDN_RESULT_ERROR, "GZIP library doesn't exist.", $file );
159
+
160
+ if ( !file_exists( $local_path ) )
161
+ return $this->_get_result( $local_path, $remote_path,
162
+ W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
163
+
164
+ $contents = @file_get_contents( $local_path );
165
+ if ( $contents === false )
166
+ return $this->_get_result( $local_path, $remote_path,
167
+ W3TC_CDN_RESULT_ERROR, 'Unable to read file.', $file );
168
+
169
+ $data = gzencode( $contents );
170
+
171
+ if ( !$force_rewrite ) {
172
+ $this->_set_error_handler();
173
+ $info = @$this->_s3->getObjectInfo( $this->_config['bucket'],
174
+ $remote_path );
175
+ $this->_restore_error_handler();
176
+
177
+ if ( $info ) {
178
+ $hash = md5( $data );
179
+ $s3_hash = ( isset( $info['hash'] ) ? $info['hash'] : '' );
180
+
181
+ if ( $hash === $s3_hash ) {
182
+ return $this->_get_result( $local_path, $remote_path,
183
+ W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
184
+ }
185
+ }
186
+ }
187
+
188
+ $headers = $this->_get_headers( $file );
189
+ $headers = array_merge( $headers, array(
190
+ 'Vary' => 'Accept-Encoding',
191
+ 'Content-Encoding' => 'gzip'
192
+ ) );
193
+
194
+ $this->_set_error_handler();
195
+ $result = @$this->_s3->putObjectString( $data, $this->_config['bucket'],
196
+ $remote_path, \S3::ACL_PUBLIC_READ, array(), $headers );
197
+ $this->_restore_error_handler();
198
+
199
+ if ( $result )
200
+ return $this->_get_result( $local_path, $remote_path,
201
+ W3TC_CDN_RESULT_OK, 'OK', $file );
202
+
203
+ return $this->_get_result( $local_path, $remote_path,
204
+ W3TC_CDN_RESULT_ERROR, sprintf( 'Unable to put object (%s).',
205
+ $this->_get_last_error() ), $file );
206
+ }
207
+
208
+ /**
209
+ * Deletes files from S3
210
+ *
211
+ * @param array $files
212
+ * @param array $results
213
+ * @return boolean
214
+ */
215
+ function delete( $files, &$results ) {
216
+ $error = null;
217
+
218
+ foreach ( $files as $file ) {
219
+ $local_path = $file['local_path'];
220
+ $remote_path = $file['remote_path'];
221
+
222
+ $this->_set_error_handler();
223
+ $result = @$this->_s3->deleteObject( $this->_config['bucket'],
224
+ $remote_path );
225
+ $this->_restore_error_handler();
226
+
227
+ if ( $result ) {
228
+ $results[] = $this->_get_result( $local_path, $remote_path,
229
+ W3TC_CDN_RESULT_OK, 'OK', $file );
230
+ } else {
231
+ $results[] = $this->_get_result( $local_path, $remote_path,
232
+ W3TC_CDN_RESULT_ERROR,
233
+ sprintf( 'Unable to delete object (%s).',
234
+ $this->_get_last_error() ), $file );
235
+ }
236
+
237
+ if ( $this->_config['compression'] ) {
238
+ $remote_path_gzip = $remote_path . $this->_gzip_extension;
239
+
240
+ $this->_set_error_handler();
241
+ $result = @$this->_s3->deleteObject( $this->_config['bucket'],
242
+ $remote_path_gzip );
243
+ $this->_restore_error_handler();
244
+
245
+ if ( $result ) {
246
+ $results[] = $this->_get_result( $local_path,
247
+ $remote_path_gzip, W3TC_CDN_RESULT_OK, 'OK', $file );
248
+ } else {
249
+ $results[] = $this->_get_result( $local_path,
250
+ $remote_path_gzip, W3TC_CDN_RESULT_ERROR,
251
+ sprintf( 'Unable to delete object (%s).',
252
+ $this->_get_last_error() ),
253
+ $file );
254
+ }
255
+ }
256
+ }
257
+
258
+ return !$this->_is_error( $results );
259
+ }
260
+
261
+ /**
262
+ * Tests S3
263
+ *
264
+ * @param string $error
265
+ * @return boolean
266
+ */
267
+ function test( &$error ) {
268
+ if ( !parent::test( $error ) ) {
269
+ return false;
270
+ }
271
+
272
+ $string = 'test_s3_' . md5( time() );
273
+
274
+ $this->_set_error_handler();
275
+
276
+ if ( !@$this->_s3->putObjectString( $string, $this->_config['bucket'],
277
+ $string, \S3::ACL_PUBLIC_READ ) ) {
278
+ $error = sprintf( 'Unable to put object (%s).',
279
+ $this->_get_last_error() );
280
+
281
+ $this->_restore_error_handler();
282
+
283
+ return false;
284
+ }
285
+
286
+ $object = @$this->_s3->getObject( $this->_config['bucket'], $string );
287
+ if ( !$object ) {
288
+ $error = sprintf( 'Unable to get object (%s).',
289
+ $this->_get_last_error() );
290
+
291
+ $this->_restore_error_handler();
292
+ return false;
293
+ }
294
+
295
+ if ( $object->body != $string ) {
296
+ $error = 'Objects are not equal.';
297
+
298
+ @$this->_s3->deleteObject( $this->_config['bucket'], $string );
299
+ $this->_restore_error_handler();
300
+
301
+ return false;
302
+ }
303
+
304
+ if ( !@$this->_s3->deleteObject( $this->_config['bucket'], $string ) ) {
305
+ $error = sprintf( 'Unable to delete object (%s).',
306
+ $this->_get_last_error() );
307
+
308
+ $this->_restore_error_handler();
309
+
310
+ return false;
311
+ }
312
+
313
+ $this->_restore_error_handler();
314
+
315
+ return true;
316
+ }
317
+
318
+ /**
319
+ * Returns CDN domain
320
+ *
321
+ * @return array
322
+ */
323
+ function get_domains() {
324
+ return (array) $this->_config['cname'];
325
+ }
326
+
327
+ /**
328
+ * Returns via string
329
+ *
330
+ * @return string
331
+ */
332
+ function get_via() {
333
+ return sprintf( 'S3-compatible: %s', parent::get_via() );
334
+ }
335
+
336
+ /**
337
+ * How and if headers should be set
338
+ *
339
+ * @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE,
340
+ * W3TC_CDN_HEADER_MIRRORING
341
+ */
342
+ function headers_support() {
343
+ return W3TC_CDN_HEADER_UPLOADABLE;
344
+ }
345
+ }
Cdn_AdminActions.php ADDED
@@ -0,0 +1,865 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_AdminActions {
7
+ private $_config = null;
8
+
9
+ function __construct() {
10
+ $this->_config = Dispatcher::config();
11
+ }
12
+
13
+ /**
14
+ * CDN queue action
15
+ *
16
+ * @return void
17
+ */
18
+ function w3tc_cdn_queue() {
19
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
20
+ $cdn_queue_action = Util_Request::get_string( 'cdn_queue_action' );
21
+ $cdn_queue_tab = Util_Request::get_string( 'cdn_queue_tab' );
22
+
23
+ $notes = array();
24
+
25
+ switch ( $cdn_queue_tab ) {
26
+ case 'upload':
27
+ case 'delete':
28
+ case 'purge':
29
+ break;
30
+
31
+ default:
32
+ $cdn_queue_tab = 'upload';
33
+ }
34
+
35
+ switch ( $cdn_queue_action ) {
36
+ case 'delete':
37
+ $cdn_queue_id = Util_Request::get_integer( 'cdn_queue_id' );
38
+ if ( !empty( $cdn_queue_id ) ) {
39
+ $w3_plugin_cdn->queue_delete( $cdn_queue_id );
40
+ $notes[] = __( 'File successfully deleted from the queue.', 'w3-total-cache' );
41
+ }
42
+ break;
43
+
44
+ case 'empty':
45
+ $cdn_queue_type = Util_Request::get_integer( 'cdn_queue_type' );
46
+ if ( !empty( $cdn_queue_type ) ) {
47
+ $w3_plugin_cdn->queue_empty( $cdn_queue_type );
48
+ $notes[] = __( 'Queue successfully emptied.', 'w3-total-cache' );
49
+ }
50
+ break;
51
+
52
+ case 'process':
53
+ $w3_plugin_cdn_normal = Dispatcher::component( 'Cdn_Plugin' );
54
+ $n = $w3_plugin_cdn_normal->cron_queue_process();
55
+ $notes[] = sprintf( __( 'Number of processed queue items: %d', 'w3-total-cache' ), $n );
56
+ break;
57
+ }
58
+
59
+ $nonce = wp_create_nonce( 'w3tc' );
60
+ $queue = $w3_plugin_cdn->queue_get();
61
+ $title = __( 'Unsuccessful file transfer queue.', 'w3-total-cache' );
62
+
63
+ include W3TC_INC_DIR . '/popup/cdn_queue.php';
64
+ }
65
+
66
+ /**
67
+ * CDN export library action
68
+ *
69
+ * @return void
70
+ */
71
+ function w3tc_cdn_export_library() {
72
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
73
+
74
+ $total = $w3_plugin_cdn->get_attachments_count();
75
+ $title = __( 'Media Library export', 'w3-total-cache' );
76
+
77
+ include W3TC_INC_DIR . '/popup/cdn_export_library.php';
78
+ }
79
+
80
+ function w3tc_cdn_flush() {
81
+ $flush = Dispatcher::component( 'CacheFlush' );
82
+ $flush->flush_all( array(
83
+ 'only' => 'cdn'
84
+ ) );
85
+
86
+ $status = $flush->execute_delayed_operations();
87
+ $errors = array();
88
+ foreach ( $status as $i ) {
89
+ if ( isset( $i['error'] ) )
90
+ $errors[] = $i['error'];
91
+ }
92
+
93
+ if ( empty( $errors ) ) {
94
+ Util_Admin::redirect( array(
95
+ 'w3tc_note' => 'flush_cdn'
96
+ ), true );
97
+ } else {
98
+ Util_Admin::redirect_with_custom_messages2( array(
99
+ 'errors' => array( 'Failed to flush CDN: ' .
100
+ implode( ', ', $errors ) )
101
+ ), true );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * CDN export library process
107
+ *
108
+ * @return void
109
+ */
110
+ function w3tc_cdn_export_library_process() {
111
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
112
+
113
+ $limit = Util_Request::get_integer( 'limit' );
114
+ $offset = Util_Request::get_integer( 'offset' );
115
+
116
+ $count = null;
117
+ $total = null;
118
+ $results = array();
119
+
120
+ $w3_plugin_cdn->export_library( $limit, $offset, $count, $total,
121
+ $results, time() + 5 );
122
+
123
+ $response = array(
124
+ 'limit' => $limit,
125
+ 'offset' => $offset,
126
+ 'count' => count( $results ),
127
+ 'total' => $total,
128
+ 'results' => $results
129
+ );
130
+
131
+ echo json_encode( $response );
132
+ }
133
+
134
+ /**
135
+ * CDN import library action
136
+ *
137
+ * @return void
138
+ */
139
+ function w3tc_cdn_import_library() {
140
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
141
+ $common = Dispatcher::component( 'Cdn_Core' );
142
+
143
+ $cdn = $common->get_cdn();
144
+
145
+ $total = $w3_plugin_cdn->get_import_posts_count();
146
+ $cdn_host = $cdn->get_domain();
147
+
148
+ $title = __( 'Media Library import', 'w3-total-cache' );
149
+
150
+ include W3TC_INC_DIR . '/popup/cdn_import_library.php';
151
+ }
152
+
153
+ /**
154
+ * CDN import library process
155
+ *
156
+ * @return void
157
+ */
158
+ function w3tc_cdn_import_library_process() {
159
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
160
+
161
+ $limit = Util_Request::get_integer( 'limit' );
162
+ $offset = Util_Request::get_integer( 'offset' );
163
+ $import_external = Util_Request::get_boolean( 'cdn_import_external' );
164
+ $config_state = Dispatcher::config_state();
165
+ $config_state->set( 'cdn.import.external', $import_external );
166
+ $config_state->save();
167
+
168
+ $count = null;
169
+ $total = null;
170
+ $results = array();
171
+
172
+ @$w3_plugin_cdn->import_library( $limit, $offset, $count, $total, $results );
173
+
174
+ $response = array(
175
+ 'limit' => $limit,
176
+ 'offset' => $offset,
177
+ 'count' => $count,
178
+ 'total' => $total,
179
+ 'results' => $results,
180
+ );
181
+
182
+ echo json_encode( $response );
183
+ }
184
+
185
+ /**
186
+ * CDN rename domain action
187
+ *
188
+ * @return void
189
+ */
190
+ function w3tc_cdn_rename_domain() {
191
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
192
+
193
+ $total = $w3_plugin_cdn->get_rename_posts_count();
194
+
195
+ $title = __( 'Modify attachment URLs', 'w3-total-cache' );
196
+
197
+ include W3TC_INC_DIR . '/popup/cdn_rename_domain.php';
198
+ }
199
+
200
+ /**
201
+ * CDN rename domain process
202
+ *
203
+ * @return void
204
+ */
205
+ function w3tc_cdn_rename_domain_process() {
206
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
207
+
208
+ $limit = Util_Request::get_integer( 'limit' );
209
+ $offset = Util_Request::get_integer( 'offset' );
210
+ $names = Util_Request::get_array( 'names' );
211
+
212
+ $count = null;
213
+ $total = null;
214
+ $results = array();
215
+
216
+ @$w3_plugin_cdn->rename_domain( $names, $limit, $offset, $count, $total, $results );
217
+
218
+ $response = array(
219
+ 'limit' => $limit,
220
+ 'offset' => $offset,
221
+ 'count' => $count,
222
+ 'total' => $total,
223
+ 'results' => $results
224
+ );
225
+
226
+ echo json_encode( $response );
227
+ }
228
+
229
+ /**
230
+ * CDN export action
231
+ *
232
+ * @return void
233
+ */
234
+ function w3tc_cdn_export() {
235
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Plugin' );
236
+
237
+ $cdn_export_type = Util_Request::get_string( 'cdn_export_type', 'custom' );
238
+
239
+ switch ( $cdn_export_type ) {
240
+ case 'includes':
241
+ $title = __( 'Includes files export', 'w3-total-cache' );
242
+ $files = $w3_plugin_cdn->get_files_includes();
243
+ break;
244
+
245
+ case 'theme':
246
+ $title = __( 'Theme files export', 'w3-total-cache' );
247
+ $files = $w3_plugin_cdn->get_files_theme();
248
+ break;
249
+
250
+ case 'minify':
251
+ $title = __( 'Minify files export', 'w3-total-cache' );
252
+ $files = $w3_plugin_cdn->get_files_minify();
253
+ break;
254
+
255
+ default:
256
+ case 'custom':
257
+ $title = __( 'Custom files export', 'w3-total-cache' );
258
+ $files = $w3_plugin_cdn->get_files_custom();
259
+ break;
260
+ }
261
+
262
+ include W3TC_INC_DIR . '/popup/cdn_export_file.php';
263
+ }
264
+
265
+ /**
266
+ * CDN export process
267
+ *
268
+ * @return void
269
+ */
270
+ function w3tc_cdn_export_process() {
271
+ $common = Dispatcher::component( 'Cdn_Core' );
272
+ $files = Util_Request::get_array( 'files' );
273
+
274
+ $upload = array();
275
+ $results = array();
276
+
277
+ foreach ( $files as $file ) {
278
+ $local_path = $common->docroot_filename_to_absolute_path( $file );
279
+ $remote_path = $common->uri_to_cdn_uri( $common->docroot_filename_to_uri( $file ) );
280
+ $d = $common->build_file_descriptor( $local_path, $remote_path );
281
+ $d['_original_id'] = $file;
282
+ $upload[] = $d;
283
+ }
284
+
285
+ $common->upload( $upload, false, $results, time() + 5 );
286
+ $output = array();
287
+
288
+ foreach ( $results as $item ) {
289
+ $file = '';
290
+ if ( isset( $item['descriptor']['_original_id'] ) )
291
+ $file = $item['descriptor']['_original_id'];
292
+
293
+ $output[] = array(
294
+ 'result' => $item['result'],
295
+ 'error' => $item['error'],
296
+ 'file' => $file
297
+ );
298
+ }
299
+
300
+ $response = array(
301
+ 'results' => $output
302
+ );
303
+
304
+ echo json_encode( $response );
305
+ }
306
+
307
+ /**
308
+ * CDN purge action
309
+ *
310
+ * @return void
311
+ */
312
+ function w3tc_cdn_purge() {
313
+ $title = __( 'Content Delivery Network (CDN): Purge Tool', 'w3-total-cache' );
314
+ $results = array();
315
+
316
+ $path = ltrim( str_replace( get_home_url(), '', get_stylesheet_directory_uri() ), '/' );
317
+ include W3TC_INC_DIR . '/popup/cdn_purge.php';
318
+ }
319
+
320
+ /**
321
+ * CDN purge post action
322
+ *
323
+ * @return void
324
+ */
325
+ function w3tc_cdn_purge_files() {
326
+ $title = __( 'Content Delivery Network (CDN): Purge Tool', 'w3-total-cache' );
327
+ $results = array();
328
+
329
+ $files = Util_Request::get_array( 'files' );
330
+
331
+ $purge = array();
332
+
333
+ $common = Dispatcher::component( 'Cdn_Core' );
334
+
335
+ foreach ( $files as $file ) {
336
+ $local_path = $common->docroot_filename_to_absolute_path( $file );
337
+ $remote_path = $common->uri_to_cdn_uri( $common->docroot_filename_to_uri( $file ) );
338
+
339
+ $purge[] = $common->build_file_descriptor( $local_path, $remote_path );
340
+ }
341
+
342
+ if ( count( $purge ) ) {
343
+ $common->purge( $purge, false, $results );
344
+ } else {
345
+ $errors[] = __( 'Empty files list.', 'w3-total-cache' );
346
+ }
347
+
348
+ $path = str_replace( get_home_url(), '', get_stylesheet_directory_uri() );
349
+ include W3TC_INC_DIR . '/popup/cdn_purge.php';
350
+ }
351
+
352
+ /**
353
+ * CDN Purge Post
354
+ *
355
+ * @return void
356
+ */
357
+ function w3tc_cdn_purge_attachment() {
358
+ $results = array();
359
+ $attachment_id = Util_Request::get_integer( 'attachment_id' );
360
+
361
+ $w3_plugin_cdn = Dispatcher::component( 'Cdn_Core_Admin' );
362
+
363
+ if ( $w3_plugin_cdn->purge_attachment( $attachment_id, $results ) ) {
364
+ Util_Admin::redirect( array(
365
+ 'w3tc_note' => 'cdn_purge_attachment'
366
+ ), true );
367
+ } else {
368
+ Util_Admin::redirect( array(
369
+ 'w3tc_error' => 'cdn_purge_attachment'
370
+ ), true );
371
+ }
372
+ }
373
+
374
+ /**
375
+ * CDN Test action
376
+ *
377
+ * @return void
378
+ */
379
+ function w3tc_cdn_test() {
380
+ $engine = Util_Request::get_string( 'engine' );
381
+ $config = Util_Request::get_array( 'config' );
382
+
383
+ //TODO: Workaround to support test case cdn/a04
384
+ if ( $engine == 'ftp' && !isset( $config['host'] ) ) {
385
+ $config = Util_Request::get_string( 'config' );
386
+ $config = json_decode( $config, true );
387
+ }
388
+
389
+ $config = array_merge( $config, array(
390
+ 'debug' => false
391
+ ) );
392
+
393
+ if ( isset( $config['domain'] ) && !is_array( $config['domain'] ) ) {
394
+ $config['domain'] = explode( ',', $config['domain'] );
395
+ }
396
+
397
+ if ( Cdn_Util::is_engine( $engine ) ) {
398
+ $result = true;
399
+ $error = null;
400
+ } else {
401
+ $result = false;
402
+ $error = __( 'Incorrect engine ' . $engine, 'w3-total-cache' );
403
+ }
404
+ if ( !isset( $config['docroot'] ) )
405
+ $config['docroot'] = Util_Environment::document_root();
406
+
407
+ if ( $result ) {
408
+ if ( $engine == 'google_drive' || $engine == 'highwinds' ||
409
+ $engine == 'rackspace_cdn' ||
410
+ $engine == 'rscf' || $engine == 's3_compatible' ) {
411
+ // those use already stored w3tc config
412
+ $w3_cdn = Dispatcher::component( 'Cdn_Core' )->get_cdn();
413
+ } else {
414
+ // those use dynamic config from the page
415
+ $w3_cdn = CdnEngine::instance( $engine, $config );
416
+ }
417
+
418
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_test' ) );
419
+
420
+ if ( $w3_cdn->test( $error ) ) {
421
+ $result = true;
422
+ $error = __( 'Test passed', 'w3-total-cache' );
423
+ } else {
424
+ $result = false;
425
+ $error = sprintf( __( 'Error: %s', 'w3-total-cache' ), $error );
426
+ }
427
+ }
428
+
429
+ $response = array(
430
+ 'result' => $result,
431
+ 'error' => $error
432
+ );
433
+
434
+ echo json_encode( $response );
435
+ }
436
+
437
+
438
+ /**
439
+ * Create container action
440
+ *
441
+ * @return void
442
+ */
443
+ function w3tc_cdn_create_container() {
444
+ $engine = Util_Request::get_string( 'engine' );
445
+ $config = Util_Request::get_array( 'config' );
446
+
447
+ $config = array_merge( $config, array(
448
+ 'debug' => false
449
+ ) );
450
+
451
+ $result = false;
452
+ $error = __( 'Incorrect type.', 'w3-total-cache' );
453
+ $container_id = '';
454
+
455
+ switch ( $engine ) {
456
+ case 's3':
457
+ case 'cf':
458
+ case 'cf2':
459
+ case 'azure':
460
+ $result = true;
461
+ break;
462
+ }
463
+
464
+ if ( $result ) {
465
+ $w3_cdn = CdnEngine::instance( $engine, $config );
466
+
467
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_container_create' ) );
468
+
469
+ if ( $w3_cdn->create_container( $container_id, $error ) ) {
470
+ $result = true;
471
+ $error = __( 'Created successfully.', 'w3-total-cache' );
472
+ } else {
473
+ $result = false;
474
+ $error = sprintf( __( 'Error: %s', 'w3-total-cache' ), $error );
475
+ }
476
+ }
477
+
478
+ $response = array(
479
+ 'result' => $result,
480
+ 'error' => $error,
481
+ 'container_id' => $container_id
482
+ );
483
+
484
+ echo json_encode( $response );
485
+ }
486
+
487
+ /**
488
+ * S3 bucket location lightbox
489
+ *
490
+ * @return void
491
+ */
492
+ function w3tc_cdn_s3_bucket_location() {
493
+ $type = Util_Request::get_string( 'type', 's3' );
494
+
495
+ $locations = array(
496
+ '' => 'US (Default)',
497
+ 'us-west-1' => __( 'US-West (Northern California)', 'w3-total-cache' ),
498
+ 'EU' => 'Europe',
499
+ 'ap-southeast-1' => __( 'AP-SouthEast (Singapore)', 'w3-total-cache' ),
500
+ );
501
+
502
+ include W3TC_INC_DIR . '/lightbox/cdn_s3_bucket_location.php';
503
+ }
504
+
505
+
506
+ /**
507
+ * Includes the manual create pull zone form.
508
+ */
509
+ function w3tc_cdn_create_netdna_maxcdn_pull_zone_form() {
510
+ $type = Util_Request::get_string( 'type', 'maxcdn' );
511
+ include W3TC_INC_DIR . '/lightbox/create_netdna_maxcdn_pull_zone.php';
512
+ }
513
+
514
+
515
+ /**
516
+ * Create NetDNA/MaxCDN pullzone
517
+ */
518
+ function w3tc_cdn_create_netdna_maxcdn_pull_zone() {
519
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
520
+ $type = Util_Request::get_string( 'type' );
521
+ $name = Util_Request::get_string( 'name' );
522
+ $label = Util_Request::get_string( 'label' );
523
+ $cdn_engine= $type;
524
+
525
+ $authorization_key = $this->_config->get_string( "cdn.$cdn_engine.authorization_key" );
526
+ $alias = $consumerkey = $consumersecret = '';
527
+
528
+ if ( $authorization_key ) {
529
+ $keys = explode( '+', $authorization_key );
530
+ if ( sizeof( $keys ) == 3 ) {
531
+ list( $alias, $consumerkey, $consumersecret ) = $keys;
532
+ }
533
+ }
534
+ $api = new \NetDNA( $alias, $consumerkey, $consumersecret );
535
+ $url = get_home_url();
536
+ $zone = array();
537
+ $zone['name'] = $name;
538
+ $zone['label'] = $label;
539
+ $zone['url'] = $url;
540
+ $zone['use_stale'] = 1;
541
+ $zone['queries'] = 1;
542
+ $zone['compress'] = 1;
543
+ $zone['backend_compress'] = 1;
544
+ try {
545
+ $response = $api->create_pull_zone( $zone );
546
+ try {
547
+ $temporary_url = "$name.$alias.netdna-cdn.com";
548
+ $test_result = -1;
549
+ if ( !$this->_config->get_array( "cdn.$cdn_engine.domain" ) ) {
550
+ $test_result = $this->test_cdn_url( $temporary_url ) ? 1 : 0;
551
+ $this->_config->set( "cdn.$cdn_engine.domain", array( $temporary_url ) );
552
+ if ( $test_result )
553
+ $this->_config->set( "cdn.enabled", true );
554
+ }
555
+ $this->_config->save();
556
+ $state = Dispatcher::config_state();
557
+ $zones = $api->get_pull_zones();
558
+ $zone_count = sizeof( $zones );
559
+ Util_Admin::make_track_call( array( 'type'=>'cdn',
560
+ 'data'=>array(
561
+ 'cdn' => $type, 'action' => 'zonecreation', 'creation' => 'manual', 'creationtime' => time()
562
+ , 'signupclick' => $state->get_integer( 'track.maxcdn_signup' )
563
+ , 'authorizeclick' => $state->get_integer( 'track.maxcdn_authorize' )
564
+ , 'validationclick' => $state->get_integer( 'track.maxcdn_validation' )
565
+ , 'total_zones' => $zone_count
566
+ , 'test' => $test_result
567
+ ) ) );
568
+ } catch ( \Exception $ex ) {}
569
+ echo json_encode( array( 'status' => 'success', 'message' => 'Pull Zone created.', 'temporary_url' => "$name.$alias.netdna-cdn.com", 'data' => $response ) );
570
+ } catch ( \Exception $ex ) {
571
+ echo json_encode( array( 'status' => 'error', 'message' => $ex->getMessage() ) );
572
+ }
573
+ }
574
+
575
+ /**
576
+ * Validates the authorization key and echos json encoded data connected with the key.
577
+ */
578
+ function w3tc_cdn_validate_authorization_key() {
579
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
580
+
581
+ $cdn_engine = Util_Request::get_string( 'type' );
582
+ $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine );
583
+ $authorization_key = Util_Request::get_string( 'authorization_key' );
584
+ $this->validate_authorization_key( $authorization_key );
585
+ $keys = explode( '+', $authorization_key );
586
+ list( $alias, $consumer_key, $consumer_secret ) = $keys;
587
+ $api = new \NetDNA( $alias, $consumer_key, $consumer_secret );
588
+ $this->validate_account( $api );
589
+ try {
590
+ $pull_zones = $api->get_zones_by_url( get_home_url() );
591
+ if ( sizeof( $pull_zones ) == 0 ) {
592
+ $result = array( 'result' => 'create' );
593
+ try {
594
+ $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key );
595
+ $this->_config->save();
596
+ } catch ( \Exception $ex ) {}
597
+ } elseif ( sizeof( $pull_zones ) == 1 ) {
598
+ $custom_domains = $api->get_custom_domains( $pull_zones[0]['id'] );
599
+ if ( sizeof( $custom_domains ) > 0 ) {
600
+ $result = array( 'result' => 'single', 'cnames' => array( $custom_domains ) );
601
+ $this->_config->set( "cdn.$cdn_engine.domain", $custom_domains );
602
+ $this->_config->set( "cdn.enabled", true );
603
+ } else {
604
+ $name = $pull_zones[0]['name'];
605
+ $result = array( 'result' => 'single', 'cnames' => array( "$name.$alias.netdna-cdn.com" ) );
606
+ }
607
+ $this->_config->set( "cdn.$cdn_engine.zone_id", $pull_zones[0]['id'] );
608
+ $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key );
609
+ $this->_config->set( "cdn.$cdn_engine.domain", $result['cnames'] );
610
+ $this->_config->save();
611
+ } else {
612
+ $zones = array();
613
+ $data = array();
614
+ foreach ( $pull_zones as $zone ) {
615
+ if ( empty( $data ) ) {
616
+ $domains = $this->test_cdn_pull_zone( $api, $zone['id'], $zone['name'], $alias );
617
+ if ( $domains ) {
618
+ $data = array( 'id' => $zone['id'], 'domains' => $domains );
619
+ $this->_config->set( "cdn.$cdn_engine.zone_id", $zone['id'] );
620
+ $this->_config->set( "cdn.$cdn_engine.domain", $domains );
621
+ }
622
+ }
623
+ $zones[] = array( 'id' => $zone['id'], 'name' => $zone['name'] );
624
+ }
625
+ $result = array( 'result' => 'many', 'zones' => $zones, 'data' => $data );
626
+ $this->_config->set( "cdn.$cdn_engine.authorization_key", $authorization_key );
627
+ $this->_config->save();
628
+ }
629
+ try {
630
+ $state = Dispatcher::config_state();
631
+ if ( $state->get_integer( 'track.maxcdn_validation', 0 ) == 0 ) {
632
+ $state->set( 'track.maxcdn_validation', time() );
633
+ $state->save();
634
+ }
635
+ } catch ( \Exception $ex ) {}
636
+ } catch ( \Exception $ex ) {
637
+ $result = array( 'result' => 'error', 'message' => $ex->getMessage() );
638
+ }
639
+ echo json_encode( $result );
640
+ exit();
641
+ }
642
+
643
+ /**
644
+ *
645
+ *
646
+ * @param NetDNA $api
647
+ * @param int $id
648
+ * @param string $name
649
+ * @param string $alias
650
+ * @return array|null
651
+ */
652
+ private function test_cdn_pull_zone( $api, $id, $name, $alias ) {
653
+ try {
654
+ $domains = $api->get_custom_domains( $id );
655
+ if ( $domains ) {
656
+ $test = true;
657
+ foreach ( $domains as $domain )
658
+ $test = $test && $this->test_cdn_url( $domain );
659
+ } else {
660
+ $url = "$name.$alias.netdna-cdn.com";
661
+ $test = $this->test_cdn_url( $url );
662
+ $domains = array( $url );
663
+ }
664
+ if ( $test )
665
+ return $domains;
666
+ } catch ( \Exception $ex ) {}
667
+ return array();
668
+ }
669
+ /**
670
+ * Validates key and echos encoded message on failure.
671
+ *
672
+ * @param unknown $authorization_key
673
+ */
674
+ private function validate_authorization_key( $authorization_key ) {
675
+ if ( empty( $authorization_key ) ) {
676
+ $result = array( 'result' => 'error', 'message' => __( 'An authorization key was not provided.', 'w3-total-cache' ) );
677
+ echo json_encode( $result );
678
+ exit();
679
+ }
680
+ $keys = explode( '+', $authorization_key );
681
+ if ( sizeof( $keys ) != 3 ) {
682
+ $result = array( 'result' => 'error', 'message' => sprintf( __( 'The provided authorization key is
683
+ not in the correct format: %s.', 'w3-total-cache' ), $authorization_key ) );
684
+ echo json_encode( $result );
685
+ exit();
686
+
687
+ }
688
+ }
689
+
690
+ /**
691
+ * Validates that the API works and echos message and exists if it fails
692
+ *
693
+ * @param NetDNA $api
694
+ * @return null|array
695
+ */
696
+ private function validate_account( $api ) {
697
+ try {
698
+ return $api->get_account();
699
+ } catch ( \Exception $ex ) {
700
+ $result = array( 'result' => 'error', 'message' => $ex->getMessage() );
701
+ echo json_encode( $result );
702
+ exit();
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Create a NetDNA or MaxCDN pull zone automatically
708
+ */
709
+ function w3tc_cdn_auto_create_netdna_maxcdn_pull_zone() {
710
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
711
+
712
+ $cdn_engine = Util_Request::get_string( 'type' );
713
+ $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine );
714
+ $authorization_key = Util_Request::get_string( 'authorization_key' );
715
+ $this->validate_authorization_key( $authorization_key );
716
+ $keys = explode( '+', $authorization_key );
717
+ list( $alias, $consumerkey, $consumersecret ) = $keys;
718
+ $url = get_home_url();
719
+ try {
720
+ $api = new \NetDNA( $alias, $consumerkey, $consumersecret );
721
+ $disable_cooker_header = $this->_config->get_boolean( 'browsercache.other.nocookies' ) ||
722
+ $this->_config->get_boolean( 'browsercache.cssjs.nocookies' );
723
+ $zone = $api->create_default_pull_zone( $url, null, null,
724
+ array( 'ignore_setcookie_header' => $disable_cooker_header ) );
725
+ $name = $zone['name'];
726
+ $temporary_url = "$name.$alias.netdna-cdn.com";
727
+ $test_result = -1;
728
+ if ( !$this->_config->get_array( "cdn.$cdn_engine.domain" ) ) {
729
+ $test_result = $this->test_cdn_url( $temporary_url ) ? 1 : 0;
730
+ $this->_config->set( "cdn.$cdn_engine.zone_id", $zone['id'] );
731
+ if ( $test_result )
732
+ $this->_config->set( "cdn.enabled", true );
733
+ $this->_config->set( "cdn.$cdn_engine.domain", array( $temporary_url ) );
734
+ }
735
+ $this->_config->save();
736
+ $state = Dispatcher::config_state();
737
+ $zones = $api->get_pull_zones();
738
+ $zone_count = sizeof( $zones );
739
+ Util_Admin::make_track_call( array( 'type'=>'cdn',
740
+ 'data'=>array(
741
+ 'cdn' => $cdn_engine, 'action' => 'zonecreation'
742
+ , 'creation' => 'manual', 'creationtime' => time()
743
+ , 'signupclick' => $state->get_integer( 'track.maxcdn_signup' )
744
+ , 'authorizeclick' => $state->get_integer( 'track.maxcdn_authorize' )
745
+ , 'validationclick' => $state->get_integer( 'track.maxcdn_validation' )
746
+ , 'total_zones' => $zone_count
747
+ , 'test' => $test_result ) ) );
748
+ $result = array( 'result' => 'single', 'cnames' => array( $temporary_url ) );
749
+ } catch ( \Exception $ex ) {
750
+ $result = array( 'result' => 'error', 'message' => sprintf( __( 'Could not create default zone.' . $ex->getMessage(), 'w3-total-cache' ) ) );
751
+ }
752
+ echo json_encode( $result );
753
+ exit();
754
+ }
755
+
756
+ private function test_cdn_url( $url ) {
757
+ $response = wp_remote_get( $url );
758
+ if ( is_wp_error( $response ) )
759
+ return false;
760
+ else {
761
+ $code = wp_remote_retrieve_response_code( $response );
762
+ return 200 == $code;
763
+ }
764
+ }
765
+ /**
766
+ * Configures the plugin to use the zone id provided in request
767
+ */
768
+ function w3tc_cdn_use_netdna_maxcdn_pull_zone() {
769
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
770
+
771
+ $cdn_engine = Util_Request::get_string( 'type' );
772
+ $this->validate_cdnengine_is_netdna_maxcdn( $cdn_engine );
773
+ $authorization_key = Util_Request::get_string( 'authorization_key' );
774
+ $this->validate_authorization_key( $authorization_key );
775
+ $zone_id = Util_Request::get_integer( 'zone_id' );
776
+ $keys = explode( '+', $authorization_key );
777
+ list( $alias, $consumer_key, $consumer_secret ) = $keys;
778
+ $api = new \NetDNA( $alias, $consumer_key, $consumer_secret );
779
+ $this->validate_account( $api );
780
+ try {
781
+ $pull_zone = $api->get_zone( $zone_id );
782
+ if ( $pull_zone ) {
783
+ $custom_domains = $api->get_custom_domains( $pull_zone['id'] );
784
+ if ( sizeof( $custom_domains ) > 0 ) {
785
+ $result = array( 'result' => 'valid', 'cnames' => array( $custom_domains ) );
786
+ $test = true;
787
+ foreach ( $custom_domains as $url )
788
+ $test = $test && $this->test_cdn_url( $url );
789
+ if ( $test )
790
+ $this->_config->set( "cdn.enabled", true );
791
+ } else {
792
+ $name = $pull_zone['name'];
793
+ $result = array( 'result' => 'valid', 'cnames' => array( "$name.$alias.netdna-cdn.com" ) );
794
+ $test = $this->test_cdn_url( "$name.$alias.netdna-cdn.com" );
795
+ if ( $test )
796
+ $this->_config->set( "cdn.enabled", true );
797
+ }
798
+ $this->_config->set( "cdn.enabled", true );
799
+ $this->_config->set( "cdn.$cdn_engine.zone_id", $pull_zone['id'] );
800
+ $this->_config->set( "cdn.$cdn_engine.domain", $result['cnames'] );
801
+ $this->_config->save();
802
+ } else {
803
+ $result = array( 'result' => 'error', 'message' => sprintf( __( 'The provided zone id was not connected to the provided authorization key.', 'w3-total-cache' ) ) );
804
+ }
805
+ } catch ( \Exception $ex ) {
806
+ $result = array( 'result' => 'error', 'message' => $ex->getMessage() );
807
+ }
808
+
809
+ echo json_encode( $result );
810
+ exit();
811
+ }
812
+
813
+ function w3tc_cdn_save_activate() {
814
+ try {
815
+ $this->_config->set( 'cdn.enabled', true );
816
+ $this->_config->save();
817
+ } catch ( \Exception $ex ) {}
818
+ Util_Environment::redirect( Util_Ui::admin_url( sprintf( 'admin.php?page=w3tc_cdn#configuration' ) ) );
819
+ }
820
+
821
+ function w3tc_cdn_maxcdn_signup() {
822
+ try {
823
+ $state = Dispatcher::config_state();
824
+ $state->set( 'track.maxcdn_signup', time() );
825
+ $state->save();
826
+ } catch ( \Exception $ex ) {}
827
+ Util_Environment::redirect( MAXCDN_SIGNUP_URL );
828
+ }
829
+
830
+ function w3tc_cdn_maxcdn_authorize() {
831
+ try {
832
+ $state = Dispatcher::config_state();
833
+ if ( $state->get_integer( 'track.maxcdn_authorize', 0 ) == 0 ) {
834
+ $state->set( 'track.maxcdn_authorize', time() );
835
+ $state->save();
836
+ }
837
+ } catch ( \Exception $ex ) {}
838
+ Util_Environment::redirect( MAXCDN_AUTHORIZE_URL );
839
+ }
840
+
841
+ function w3tc_cdn_netdna_authorize() {
842
+ try {
843
+ $state = Dispatcher::config_state();
844
+ if ( $state->get_integer( 'track.maxcdn_authorize', 0 ) == 0 ) {
845
+ $state->set( 'track.maxcdn_authorize', time() );
846
+ $state->save();
847
+ }
848
+ } catch ( \Exception $ex ) {}
849
+ Util_Environment::redirect( NETDNA_AUTHORIZE_URL );
850
+ }
851
+
852
+ /**
853
+ * Validates cdn engine and echos message and exists if it fails
854
+ *
855
+ * @param unknown $cdn_engine
856
+ */
857
+ private function validate_cdnengine_is_netdna_maxcdn( $cdn_engine ) {
858
+ if ( !in_array( $cdn_engine, array( 'netdna', 'maxcdn' ) ) ) {
859
+ $result = array( 'result' => 'notsupported', 'message' => sprintf( __( '%s is not supported for Pull Zone
860
+ selection.', 'w3-total-cache' ), $cdn_engine ) );
861
+ echo json_encode( $result );
862
+ exit();
863
+ }
864
+ }
865
+ }
Cdn_AdminNotes.php ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_AdminNotes {
7
+ /**
8
+ *
9
+ *
10
+ * @param Config $config
11
+ * @return array
12
+ */
13
+ public function w3tc_notes( $notes ) {
14
+ $config = Dispatcher::config();
15
+ $state = Dispatcher::config_state();
16
+
17
+ $page = Util_Request::get_string( 'page' );
18
+
19
+ if ( !Cdn_Util::is_engine_mirror( $config->get_string( 'cdn.engine' ) ) ) {
20
+ /**
21
+ * Show notification after theme change
22
+ */
23
+ if ( $state->get_boolean( 'cdn.show_note_theme_changed' ) ) {
24
+ $notes['cdn_theme_changed'] = sprintf(
25
+ __( 'The active theme has changed, please %s now to ensure proper operation. %s',
26
+ 'w3-total-cache' ),
27
+ Util_Ui::button_popup(
28
+ __( 'upload active theme files', 'w3-total-cache' ),
29
+ 'cdn_export',
30
+ 'cdn_export_type=theme' ),
31
+ Util_Ui::button_hide_note2( array(
32
+ 'w3tc_default_config_state' => 'y',
33
+ 'key' => 'cdn.show_note_theme_changed',
34
+ 'value' => 'false' ) ) );
35
+ }
36
+
37
+ /**
38
+ * Show notification after WP upgrade
39
+ */
40
+ if ( $state->get_boolean( 'cdn.show_note_wp_upgraded' ) ) {
41
+ $notes['cdn_wp_upgraded'] = sprintf(
42
+ __( 'Upgraded WordPress? Please %s files now to ensure proper operation. %s',
43
+ 'w3-total-cache' ),
44
+ Util_Ui::button_popup( 'upload wp-includes', 'cdn_export',
45
+ 'cdn_export_type=includes' ),
46
+ Util_Ui::button_hide_note2( array(
47
+ 'w3tc_default_config_state' => 'y',
48
+ 'key' => 'cdn.show_note_wp_upgraded',
49
+ 'value' => 'false' ) ) );
50
+ }
51
+
52
+ /**
53
+ * Show notification after CDN enable
54
+ */
55
+ if ( $state->get_boolean( 'cdn.show_note_cdn_upload' ) ||
56
+ $state->get_boolean( 'cdn.show_note_cdn_reupload' ) ) {
57
+ $cdn_upload_buttons = array();
58
+
59
+ if ( $config->get_boolean( 'cdn.includes.enable' ) ) {
60
+ $cdn_upload_buttons[] = Util_Ui::button_popup(
61
+ 'wp-includes', 'cdn_export', 'cdn_export_type=includes' );
62
+ }
63
+
64
+ if ( $config->get_boolean( 'cdn.theme.enable' ) ) {
65
+ $cdn_upload_buttons[] = Util_Ui::button_popup(
66
+ 'theme files', 'cdn_export', 'cdn_export_type=theme' );
67
+ }
68
+
69
+ if ( $config->get_boolean( 'minify.enabled' ) &&
70
+ $config->get_boolean( 'cdn.minify.enable' ) &&
71
+ !$config->get_boolean( 'minify.auto' ) ) {
72
+ $cdn_upload_buttons[] = Util_Ui::button_popup(
73
+ 'minify files', 'cdn_export',
74
+ 'cdn_export_type=minify' );
75
+ }
76
+
77
+ if ( $config->get_boolean( 'cdn.custom.enable' ) ) {
78
+ $cdn_upload_buttons[] = Util_Ui::button_popup(
79
+ 'custom files', 'cdn_export', 'cdn_export_type=custom' );
80
+ }
81
+
82
+ if ( $state->get_boolean( 'cdn.show_note_cdn_upload' ) ) {
83
+ $notes[] = sprintf(
84
+ __( 'Make sure to %s and upload the %s, files to the <acronym title="Content Delivery Network">CDN</acronym> to ensure proper operation. %s',
85
+ 'w3-total-cache' ),
86
+ Util_Ui::button_popup( 'export the media library',
87
+ 'cdn_export_library' ),
88
+ implode( ', ', $cdn_upload_buttons ),
89
+ Util_Ui::button_hide_note2( array(
90
+ 'w3tc_default_config_state' => 'y',
91
+ 'key' => 'cdn.show_note_cdn_upload',
92
+ 'value' => 'false' ) ) );
93
+ }
94
+
95
+ if ( $state->get_boolean( 'cdn.show_note_cdn_reupload' ) ) {
96
+ $notes[] = sprintf(
97
+ __( 'Settings that affect Browser Cache settings for files hosted by the CDN have been changed. To apply the new settings %s and %s. %s',
98
+ 'w3-total-cache' ),
99
+ Util_Ui::button_popup(
100
+ __( 'export the media library', 'w3-total-cache' ),
101
+ 'cdn_export_library' ),
102
+ implode( ', ', $cdn_upload_buttons ),
103
+ Util_Ui::button_hide_note2( array(
104
+ 'w3tc_default_config_state' => 'y',
105
+ 'key' => 'cdn.show_note_cdn_reupload',
106
+ 'value' => 'false' ) ) );
107
+ }
108
+ }
109
+ }
110
+
111
+ if ( in_array( $config->get_string( 'cdn.engine' ), array( 'netdna', 'maxcdn' ) ) &&
112
+ !$state->get_boolean( 'cdn.hide_note_maxcdn_whitelist_ip' ) &&
113
+ $state->get_integer( 'track.maxcdn_authorize' ) == 0 &&
114
+ $config->get_string( 'cdn.' . $config->get_string( 'cdn.engine' ) .'.authorization_key' ) ) {
115
+ $notes[] = sprintf(
116
+ __( 'Make sure to whitelist your servers IPs. Follow the instructions on %s. The IP for this server is %s. %s', 'w3-total-cache' ),
117
+ '<a href="http://support.maxcdn.com/tutorials/how-to-whitelist-your-server-ip-to-use-the-api/">MaxCDN</a>',
118
+ $_SERVER['SERVER_ADDR'],
119
+ Util_Ui::button_hide_note2( array(
120
+ 'w3tc_default_config_state' => 'y',
121
+ 'key' => 'cdn.hide_note_maxcdn_whitelist_ip',
122
+ 'value' => 'true' ) ) );
123
+ }
124
+
125
+ /**
126
+ * Check CURL extension
127
+ */
128
+ if ( !$state->get_boolean( 'cdn.hide_note_no_curl' ) &&
129
+ !function_exists( 'curl_init' ) ) {
130
+ $notes[] = sprintf(
131
+ __( 'The <strong>CURL PHP</strong> extension is not available. Please install it to enable S3 or CloudFront functionality. %s',
132
+ 'w3-total-cache' ),
133
+ Util_Ui::button_hide_note2( array(
134
+ 'w3tc_default_config_state' => 'y',
135
+ 'key' => 'cdn.hide_note_no_curl',
136
+ 'value' => 'true' ) ) );
137
+ }
138
+
139
+ return $notes;
140
+ }
141
+
142
+ function w3tc_errors( $errors ) {
143
+ $c = Dispatcher::config();
144
+ $state = Dispatcher::config_state();
145
+ $cdn_engine = $c->get_string( 'cdn.engine' );
146
+
147
+ if ( Cdn_Util::is_engine_push( $cdn_engine ) ) {
148
+ /**
149
+ * Show notification if upload queue is not empty
150
+ */
151
+ try {
152
+ if ( !( $error = get_transient( 'w3tc_cdn_error' ) ) &&
153
+ !$this->_is_queue_empty() ) {
154
+ $errors['cdn_unsuccessful_queue'] = sprintf(
155
+ __( 'The %s has unresolved errors. Empty the queue to restore normal operation.',
156
+ 'w3-total-cache' ),
157
+ Util_Ui::button_popup(
158
+ __( 'unsuccessful transfer queue', 'w3-total-cache' ), 'cdn_queue' ) );
159
+ } elseif ( $error ) {
160
+ $errors['cdn_generic'] = $error;
161
+ }
162
+ } catch ( \Exception $ex ) {
163
+ $errors[] = $ex->getMessage();
164
+ set_transient( 'w3tc_cdn_error', $ex->getMessage(), 30 );
165
+ }
166
+
167
+ /**
168
+ * Check upload settings
169
+ */
170
+ $upload_info = Util_Http::upload_info();
171
+
172
+ if ( !$upload_info ) {
173
+ $upload_path = get_option( 'upload_path' );
174
+ $upload_path = trim( $upload_path );
175
+
176
+ if ( empty( $upload_path ) ) {
177
+ $upload_path = WP_CONTENT_DIR . '/uploads';
178
+
179
+ $errors['cdn_uploads_folder_empty'] = sprintf(
180
+ __( 'The uploads directory is not available. Default WordPress directories will be created: <strong>%s</strong>.',
181
+ 'w3-total-cache' ),
182
+ $upload_path );
183
+ }
184
+
185
+ if ( !Util_Environment::is_wpmu() ) {
186
+ $errors['cdn_uploads_folder_not_found'] = sprintf(
187
+ __( 'The uploads path found in the database (%s) is inconsistent with the actual path. Please manually adjust the upload path either in miscellaneous settings or if not using a custom path %s automatically to resolve the issue.',
188
+ 'w3-total-cache' ),
189
+ $upload_path,
190
+ Util_Ui::button_link( __( 'update the path', 'w3-total-cache' ),
191
+ Util_Ui::url( array(
192
+ 'w3tc_config_update_upload_path' => 'y' ) ) ) );
193
+ }
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Check CDN settings
199
+ */
200
+ $error = '';
201
+ switch ( true ) {
202
+ case ( $cdn_engine == 'ftp' && !count( $c->get_array( 'cdn.ftp.domain' ) ) ):
203
+ $errors['cdn_ftp_empty'] = __( 'A configuration issue prevents <acronym title="Content Delivery Network">CDN</acronym> from working:
204
+ The <strong>"Replace default hostname with"</strong>
205
+ field cannot be empty. Enter <acronym
206
+ title="Content Delivery Network">CDN</acronym>
207
+ provider hostname <a href="?page=w3tc_cdn#configuration">here</a>.
208
+ <em>(This is the hostname used in order to view objects
209
+ in a browser.)</em>', 'w3-total-cache' );
210
+ break;
211
+
212
+ case ( $cdn_engine == 's3' && ( $c->get_string( 'cdn.s3.key' ) == '' || $c->get_string( 'cdn.s3.secret' ) == '' || $c->get_string( 'cdn.s3.bucket' ) == '' ) ):
213
+ $error = __( 'The <strong>"Access key", "Secret key" and "Bucket"</strong> fields cannot be empty.', 'w3-total-cache' );
214
+ break;
215
+
216
+ case ( $cdn_engine == 'cf' && ( $c->get_string( 'cdn.cf.key' ) == '' || $c->get_string( 'cdn.cf.secret' ) == '' || $c->get_string( 'cdn.cf.bucket' ) == '' || ( $c->get_string( 'cdn.cf.id' ) == '' && !count( $c->get_array( 'cdn.cf.cname' ) ) ) ) ):
217
+ $error = __( 'The <strong>"Access key", "Secret key", "Bucket" and "Replace default hostname with"</strong> fields cannot be empty.', 'w3-total-cache' );
218
+ break;
219
+
220
+ case ( $cdn_engine == 'cf2' && ( $c->get_string( 'cdn.cf2.key' ) == '' || $c->get_string( 'cdn.cf2.secret' ) == '' || ( $c->get_string( 'cdn.cf2.id' ) == '' && !count( $c->get_array( 'cdn.cf2.cname' ) ) ) ) ):
221
+ $error = __( 'The <strong>"Access key", "Secret key" and "Replace default hostname with"</strong> fields cannot be empty.', 'w3-total-cache' );
222
+ break;
223
+
224
+ case ( $cdn_engine == 'rscf' && ( $c->get_string( 'cdn.rscf.user' ) == '' || $c->get_string( 'cdn.rscf.key' ) == '' || $c->get_string( 'cdn.rscf.container' ) == '' ) ):
225
+ $error = __( 'The <strong>"Username", "API key", "Container" and "Replace default hostname with"</strong> fields cannot be empty.', 'w3-total-cache' );
226
+ break;
227
+
228
+ case ( $cdn_engine == 'azure' && ( $c->get_string( 'cdn.azure.user' ) == '' || $c->get_string( 'cdn.azure.key' ) == '' || $c->get_string( 'cdn.azure.container' ) == '' ) ):
229
+ $error = __( 'The <strong>"Account name", "Account key" and "Container"</strong> fields cannot be empty.', 'w3-total-cache' );
230
+ break;
231
+
232
+ case ( $cdn_engine == 'mirror' && !count( $c->get_array( 'cdn.mirror.domain' ) ) ):
233
+ $error = __( 'The <strong>"Replace default hostname with"</strong> field cannot be empty.', 'w3-total-cache' );
234
+ break;
235
+
236
+ case ( $cdn_engine == 'netdna' ):
237
+ $fields = array();
238
+ if ( $c->get_string( 'cdn.netdna.authorization_key' ) == '' )
239
+ $fields[] = '"' . __( 'Authorization key', 'w3-total-cache' ) . '"';
240
+
241
+ if ( !count( $c->get_array( 'cdn.netdna.domain' ) ) )
242
+ $fields[] = '"' . __( 'Replace default hostname with', 'w3-total-cache' ) . '"';
243
+
244
+ if ( $fields ) {
245
+ $error = sprintf( __( 'The <strong>%s</strong> field(s) cannot be empty.', 'w3-total-cache' ),
246
+ implode( __( ' and ', 'w3-total-cache' ), $fields ) );
247
+ }
248
+
249
+ if ( $c->get_string( 'cdn.netdna.authorization_key' ) != '' &&
250
+ sizeof( explode( '+', $c->get_string( 'cdn.netdna.authorization_key' ) ) ) != 3 )
251
+ $error .= __( 'The <strong>"Authorization key"</strong> is not correct.', 'w3-total-cache' );
252
+ elseif ( $c->get_integer( 'cdn.netdna.zone_id', 0 ) <= 0 )
253
+ $error .= __( 'You need to select / create a pull zone.', 'w3-total-cache' );
254
+ break;
255
+
256
+ case ( $cdn_engine == 'maxcdn' ):
257
+ $fields = array();
258
+ if ( $c->get_string( 'cdn.maxcdn.authorization_key' ) == '' )
259
+ $fields[] = '"' . __( 'Authorization key', 'w3-total-cache' ) . '"';
260
+
261
+ if ( !count( $c->get_array( 'cdn.maxcdn.domain' ) ) )
262
+ $fields[] = '"' . __( 'Replace default hostname with', 'w3-total-cache' ) . '"';
263
+
264
+ if ( $fields ) {
265
+ $error = sprintf( __( 'The <strong>%s</strong> field(s) cannot be empty.', 'w3-total-cache' ),
266
+ implode( __( ' and ', 'w3-total-cache' ), $fields ) );
267
+ }
268
+
269
+ if ( $c->get_string( 'cdn.maxcdn.authorization_key' ) != '' &&
270
+ sizeof( explode( '+', $c->get_string( 'cdn.maxcdn.authorization_key' ) ) ) != 3 )
271
+ $error .= __( 'The <strong>"Authorization key"</strong> is not correct.', 'w3-total-cache' );
272
+ elseif ( $c->get_integer( 'cdn.maxcdn.zone_id', 0 ) <= 0 )
273
+ $error .= __( 'You need to select / create a pull zone.', 'w3-total-cache' );
274
+
275
+ break;
276
+
277
+ case ( $cdn_engine == 'cotendo' && !count( $c->get_array( 'cdn.cotendo.domain' ) ) ):
278
+ $error = __( 'The <strong>"Replace default hostname with"</strong> field cannot be empty.', 'w3-total-cache' );
279
+ break;
280
+
281
+ case ( $cdn_engine == 'edgecast' && !count( $c->get_array( 'cdn.edgecast.domain' ) ) ):
282
+ $error = __( 'The <strong>"Replace default hostname with"</strong> field cannot be empty.', 'w3-total-cache' );
283
+ break;
284
+
285
+ case ( $cdn_engine == 'att' && !count( $c->get_array( 'cdn.att.domain' ) ) ):
286
+ $error = __( 'The <strong>"Replace default hostname with"</strong> field cannot be empty.', 'w3-total-cache' );
287
+ break;
288
+
289
+ case ( $cdn_engine == 'akamai' && !count( $c->get_array( 'cdn.akamai.domain' ) ) ):
290
+ $error = 'The <strong>"Replace default hostname with"</strong> field cannot be empty.';
291
+ break;
292
+ }
293
+
294
+ if ( $error ) {
295
+ $errors['cdn_not_configured'] = __( 'A configuration issue prevents <acronym title="Content Delivery Network">CDN</acronym> from working: ', 'w3-total-cache' ) . $error . __( ' <a href="?page=w3tc_cdn#configuration">Specify it here</a>.', 'w3-total-cache' );
296
+ }
297
+
298
+ return $errors;
299
+ }
300
+
301
+
302
+ /**
303
+ * Returns true if upload queue is empty
304
+ *
305
+ * @return bool
306
+ * @throws Exception
307
+ */
308
+ private function _is_queue_empty() {
309
+ global $wpdb;
310
+ $wpdb->hide_errors();
311
+ $sql = sprintf( 'SELECT COUNT(*) FROM %s', $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE );
312
+ $result = $wpdb->get_var( $sql );
313
+ if ( ( $error = $wpdb->last_error ) ) {
314
+ if ( strpos( $error, "doesn't exist" ) !== false ) {
315
+ $url = is_network_admin() ? network_admin_url( 'admin.php?page=w3tc_install' ) : admin_url( 'admin.php?page=w3tc_install' );
316
+ throw new \Exception( sprintf(
317
+ __( 'Encountered issue with CDN: %s. See %s for instructions of creating correct table.', 'w3-total-cache' ),
318
+ $wpdb->last_error,
319
+ '<a href="' . $url . '">' . __( 'Install page', 'w3-total-cache' ) . '</a>' ) );
320
+ }
321
+ else
322
+ throw new \Exception( sprintf( __( 'Encountered issue with CDN: %s.', 'w3-total-cache' ), $wpdb->last_error ) );
323
+ }
324
+ return $result == 0;
325
+ }
326
+ }
Cdn_CacheFlush.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * CDN cache purge object
6
+ */
7
+
8
+ /**
9
+ * class Cdn_CacheFlush
10
+ */
11
+ class Cdn_CacheFlush {
12
+ /**
13
+ * Advanced cache config
14
+ */
15
+ var $_config = null;
16
+
17
+
18
+ /**
19
+ * Array of urls to flush
20
+ *
21
+ * @var array
22
+ */
23
+ private $flush_operation_requested = false;
24
+
25
+ /**
26
+ * PHP5 Constructor
27
+ */
28
+ function __construct() {
29
+ $this->_config = Dispatcher::config();
30
+ }
31
+
32
+ /**
33
+ * Purges everything from CDNs that supports it
34
+ */
35
+ function purge_all() {
36
+ $this->flush_operation_requested = true;
37
+ return true;
38
+ }
39
+
40
+ /**
41
+ * Purge a single url
42
+ *
43
+ * @param unknown $url
44
+ */
45
+ function purge_url( $url ) {
46
+ $common = Dispatcher::component( 'Cdn_Core' );
47
+ $results = array();
48
+ $files = array();
49
+ $parsed = parse_url( $url );
50
+ $local_site_path = isset( $parsed['path'] )? ltrim( $parsed['path'], '/' ) : '';
51
+ $remote_path = $common->uri_to_cdn_uri( $local_site_path );
52
+ $files[] = $common->build_file_descriptor( $local_site_path, $remote_path );
53
+ $this->_flushed_urls[] = $url;
54
+ $common->purge( $files, false, $results );
55
+ }
56
+
57
+ /**
58
+ * Clears global and repeated urls
59
+ */
60
+ function purge_post_cleanup() {
61
+ if ( $this->flush_operation_requested ) {
62
+ $common = Dispatcher::component( 'Cdn_Core' );
63
+ $results = array();
64
+ $common->purge_all( $results );
65
+
66
+ $count = 999;
67
+
68
+ $this->flush_operation_requested = false;
69
+ }
70
+
71
+ return $count;
72
+ }
73
+ }
Cdn_CloudFrontFsd_Api.php ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_CloudFrontFsd_Api {
5
+ private $access_key; // AWS Access key
6
+ private $secret_Key; // AWS Secret key
7
+ private $api_host; // AWS host where API is located
8
+
9
+
10
+ public function __construct( $access_key = null, $secret_key = null,
11
+ $api_host = 'cloudfront.amazonaws.com' ) {
12
+ $this->access_key = $access_key;
13
+ $this->secret_key = $secret_key;
14
+ $this->api_host = $api_host;
15
+ }
16
+
17
+
18
+
19
+ /**
20
+ * Get a list of CloudFront distributions
21
+ */
22
+ public function distributions_list() {
23
+ $response = $this->request( 'GET', '/2014-11-06/distribution' );
24
+ $data = $response['body_array'];
25
+
26
+ $data = $this->fix_array( $data, array(
27
+ 'Items', 'DistributionSummary' ) );
28
+
29
+ if ( isset( $data['Items']['DistributionSummary'] ) ) {
30
+ foreach ( $data['Items']['DistributionSummary'] as $key => $value ) {
31
+ $data['Items']['DistributionSummary'][$key] =
32
+ $this->fix_distribution(
33
+ $data['Items']['DistributionSummary'][$key] );
34
+ }
35
+ }
36
+
37
+ return $data;
38
+ }
39
+
40
+
41
+
42
+ /**
43
+ * Get a list of CloudFront distributions
44
+ */
45
+ public function distribution_get( $id ) {
46
+ $response = $this->request( 'GET', '/2014-11-06/distribution/' . $id );
47
+ $data = $response['body_array'];
48
+
49
+ if ( isset( $data['DistributionConfig'] ) )
50
+ $data['DistributionConfig'] = $this->fix_distribution(
51
+ $data['DistributionConfig'] );
52
+
53
+ return $data;
54
+ }
55
+
56
+
57
+
58
+ /**
59
+ * $distribution
60
+ * origin
61
+ */
62
+ public function distribution_create( $distribution ) {
63
+ if ( !isset( $distribution['CallerReference'] ) )
64
+ $distribution['CallerReference'] = rand();
65
+ if ( !isset( $distribution['Enabled'] ) )
66
+ $distribution['Enabled'] = 'true';
67
+
68
+ $data =
69
+ '<?xml version="1.0" encoding="UTF-8"?>' .
70
+ '<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
71
+ $this->array_to_xml( $distribution ) .
72
+ '</DistributionConfig>';
73
+
74
+ $response = $this->request( 'POST', '/2014-11-06/distribution', $data );
75
+ $response_data = $response['body_array'];
76
+
77
+ return $response_data;
78
+ }
79
+
80
+
81
+
82
+ /**
83
+ * $distribution
84
+ * origin
85
+ */
86
+ public function distribution_update( $distribution_id, $distribution ) {
87
+ $get_response = $this->request( 'GET', '/2014-11-06/distribution/' .
88
+ $distribution_id );
89
+ $get_distribution = $get_response['body_array'];
90
+
91
+ $c = $get_distribution['DistributionConfig'];
92
+
93
+ // update values
94
+ foreach ( $distribution as $key => $value ) {
95
+ if ( $key == 'DefaultCacheBehavior' ) {
96
+ // inherit inner values of that setting too
97
+ foreach ( $distribution[$key] as $key2 => $value2 ) {
98
+ $c[$key][$key2] = $value2;
99
+ }
100
+ } else {
101
+ $c[$key] = $value;
102
+ }
103
+ }
104
+
105
+ $data =
106
+ '<?xml version="1.0" encoding="UTF-8"?>' .
107
+ '<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
108
+ $this->array_to_xml( $c ) .
109
+ '</DistributionConfig>';
110
+
111
+ $response = $this->request( 'PUT',
112
+ '/2014-11-06/distribution/' . $distribution_id . '/config', $data,
113
+ array( 'If-Match' => $get_response['headers']['etag'] ) );
114
+ $data = $response['body_array'];
115
+
116
+ if ( isset( $data['DistributionConfig'] ) )
117
+ $data['DistributionConfig'] = $this->fix_distribution(
118
+ $data['DistributionConfig'] );
119
+
120
+ return $data;
121
+ }
122
+
123
+
124
+
125
+ /**
126
+ * Invalidate cache
127
+ */
128
+ public function invalidation_create( $distribution_id, $uris ) {
129
+ $data =
130
+ '<?xml version="1.0" encoding="UTF-8"?>' .
131
+ '<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
132
+ '<Paths>' .
133
+ '<Quantity>' . count( $uris ) . '</Quantity>' .
134
+ $this->array_to_xml( array(
135
+ 'Items' => array(
136
+ 'Path' => $uris
137
+ )
138
+ ) ) .
139
+ '</Paths>' .
140
+ '<CallerReference>' . rand() . '</CallerReference>' .
141
+ '</InvalidationBatch>';
142
+
143
+ $response = $this->request( 'POST',
144
+ '/2014-11-06/distribution/' . $distribution_id . '/invalidation', $data );
145
+ $response_data = $response['body_array'];
146
+
147
+ return $response_data;
148
+ }
149
+
150
+ /**
151
+ * Constructor
152
+ *
153
+ * @param string $verb Verb
154
+ * @param string $bucket Bucket name
155
+ * @param string $uri Object URI
156
+ * @return mixed
157
+ */
158
+ private function request( $method, $uri = '', $data = '', $headers = array() ) {
159
+ $url = 'https://' . $this->api_host . $uri;
160
+
161
+ $headers['Host'] = $this->api_host;
162
+ $headers['x-amz-date'] = gmdate( 'Ymd\THis\Z', time() );
163
+
164
+ if ( $method == "POST" || $method == "PUT" ) {
165
+ $headers['Content-Type'] = 'application/xml; charset=utf-8';
166
+ $headers['Content-Length'] = strlen( $data );
167
+ } else {
168
+ $data = '';
169
+ }
170
+
171
+ $headers['Authorization'] = $this->calculateSignature( $method, $uri,
172
+ $headers, $data );
173
+
174
+
175
+ // do the request
176
+ $request = array(
177
+ 'sslverify' => false,
178
+ 'headers' => $headers,
179
+ 'method' => $method,
180
+ 'body' => $data
181
+ );
182
+
183
+ $response = wp_remote_request( $url, $request );
184
+
185
+ // handle response
186
+ if ( substr( $response['body'], 0, 5 ) != '<?xml' )
187
+ throw new \Exception( 'Unexpected non-xml response from service received' );
188
+
189
+ $xml = simplexml_load_string( $response['body'] );
190
+ $json = json_encode( $xml );
191
+ $body_array = json_decode( $json, TRUE );
192
+ $response['body_array'] = $body_array;
193
+
194
+ if ( isset( $body_array['Error'] ) && isset( $body_array['Error']['Message'] ) )
195
+ throw new \Exception( $body_array['Error']['Message'] );
196
+
197
+ return $response;
198
+ }
199
+
200
+
201
+
202
+ public function calculateSignature( $method, $uri, $headers, $data ) {
203
+ $short_date = substr( $headers['x-amz-date'], 0, 8 );
204
+
205
+ // Parse the service and region or use one that is explicitly set
206
+ $region = 'us-east-1';
207
+ $service = 'cloudfront';
208
+
209
+ $credential_scope = $short_date . '/' . $region . '/' . $service .
210
+ '/aws4_request';
211
+
212
+
213
+ // calc payload
214
+ if ( empty( $data ) )
215
+ $payload = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
216
+ else
217
+ $payload = hash( 'sha256', $data );
218
+
219
+
220
+ // create canonical request
221
+ $canonical_headers = array(
222
+ 'host:' . $headers['Host'],
223
+ 'x-amz-date:' . $headers['x-amz-date']
224
+ );
225
+
226
+ $signed_headers = 'host;x-amz-date';
227
+ $canonical_request = $method . "\n" .
228
+ $this->canonicalize_uri( $uri ) . "\n" .
229
+ '' /* query string */ . "\n" .
230
+ implode( "\n", $canonical_headers ) . "\n\n" .
231
+ $signed_headers . "\n" .
232
+ $payload;
233
+
234
+ $string_to_sign =
235
+ "AWS4-HMAC-SHA256\n" .
236
+ $headers['x-amz-date'] . "\n" .
237
+ $credential_scope . "\n" .
238
+ hash( 'sha256', $canonical_request );
239
+
240
+ // Calculate the signing key using a series of derived keys
241
+ $signingKey = $this->get_signing_key( $short_date, $region, $service,
242
+ $this->secret_key );
243
+ $signature = hash_hmac( 'sha256', $string_to_sign, $signingKey );
244
+
245
+ return "AWS4-HMAC-SHA256 " .
246
+ "Credential={$this->access_key}/{$credential_scope}, " .
247
+ "SignedHeaders={$signed_headers}, Signature={$signature}";
248
+ }
249
+
250
+
251
+
252
+
253
+ private function canonicalize_uri( $uri ) {
254
+ $doubleEncoded = rawurlencode( ltrim( $uri, '/' ) );
255
+ return '/' . str_replace( '%2F', '/', $doubleEncoded );
256
+ }
257
+
258
+
259
+
260
+ private function get_signing_key( $short_date, $region, $service, $secretKey ) {
261
+ $dateKey = hash_hmac( 'sha256', $short_date, 'AWS4' . $secretKey, true );
262
+ $regionKey = hash_hmac( 'sha256', $region, $dateKey, true );
263
+ $serviceKey = hash_hmac( 'sha256', $service, $regionKey, true );
264
+
265
+ return hash_hmac( 'sha256', 'aws4_request', $serviceKey, true );
266
+ }
267
+
268
+
269
+
270
+ private function array_to_xml( $a ) {
271
+ if ( !is_array( $a ) )
272
+ return $a;
273
+
274
+ $s = '';
275
+ foreach ( $a as $key => $value ) {
276
+ if ( is_array( $value ) && isset( $value[0] ) ) {
277
+ // number-indexed array, serialized as list
278
+ foreach ( $value as $array_item ) {
279
+ $s .=
280
+ '<' . $key . '>' .
281
+ $this->array_to_xml( $array_item ) .
282
+ '</' . $key . '>';
283
+ }
284
+ } else {
285
+ $s .=
286
+ '<' . $key . '>' .
287
+ $this->array_to_xml( $value ) .
288
+ '</' . $key . '>';
289
+ }
290
+ }
291
+
292
+ return $s;
293
+ }
294
+
295
+
296
+
297
+ /**
298
+ * XML parsing suffers common problem of array recognition
299
+ * <items><item>a</></> is accessible via $data['items']['item']
300
+ * but
301
+ * <items><item>a</><item>b</></> is accessible
302
+ * via $data['items'][$n]['item']
303
+ *
304
+ * by knowing tag we can try to guess that it contains only 1 element
305
+ * and turn it to array so that it will be always accessible via 2nd way
306
+ */
307
+ private function fix_array( $response, $keys ) {
308
+ $a = &$response;
309
+
310
+ for ( $n = 0; $n < count( $keys ) - 1; $n++ ) {
311
+ $key = $keys[$n];
312
+ if ( !isset( $a[$key] ) )
313
+ break;
314
+ $a = &$a[$key];
315
+ }
316
+
317
+ $last_key = $keys[count( $keys ) - 1];
318
+ if ( isset( $a[$last_key] ) ) {
319
+ if ( !isset( $a[$last_key][0] ) || is_string( $a[$last_key] ) ) {
320
+ $a[$last_key] = array(
321
+ $a[$last_key]
322
+ );
323
+ }
324
+ }
325
+
326
+ return $response;
327
+ }
328
+
329
+
330
+
331
+ private function fix_distribution( $data ) {
332
+ $data = $this->fix_array( $data, array( 'Aliases', 'Items', 'CNAME' ) );
333
+ $data = $this->fix_array( $data, array( 'Origins', 'Items', 'Origin' ) );
334
+ $data = $this->fix_array( $data, array( 'DefaultCacheBehavior',
335
+ 'ForwardedValues', 'Headers', 'Items', 'Name' ) );
336
+
337
+ return $data;
338
+ }
339
+ }
Cdn_CloudFrontFsd_Engine.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_CloudFrontFsd_Engine {
7
+ private $access_key;
8
+ private $secret_key;
9
+ private $distribution_id;
10
+
11
+
12
+
13
+ function __construct( $config = array() ) {
14
+ $this->access_key = $config['access_key'];
15
+ $this->secret_key = $config['secret_key'];
16
+ $this->distribution_id = $config['distribution_id'];
17
+ }
18
+
19
+
20
+
21
+ function flush_urls( $urls ) {
22
+ if ( empty( $this->access_key ) || empty( $this->secret_key ) ||
23
+ empty( $this->distribution_id ) )
24
+ throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) );
25
+
26
+ $api = new Cdn_CloudFrontFsd_Api( $this->access_key, $this->secret_key );
27
+ $uris = array();
28
+ foreach ( $urls as $url ) {
29
+ $parsed = parse_url( $url );
30
+ $relative_url =
31
+ ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
32
+ ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
33
+ $uris[] = $relative_url;
34
+ }
35
+
36
+ $api->invalidation_create( $this->distribution_id, $uris );
37
+ }
38
+
39
+
40
+
41
+ /**
42
+ * Flushes CDN completely
43
+ */
44
+ function flush_all() {
45
+ if ( empty( $this->access_key ) || empty( $this->secret_key ) ||
46
+ empty( $this->distribution_id ) )
47
+ throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) );
48
+
49
+ $api = new Cdn_CloudFrontFsd_Api( $this->access_key, $this->secret_key );
50
+ $uris = array( '/*' );
51
+
52
+ $api->invalidation_create( $this->distribution_id, $uris );
53
+ }
54
+ }
Cdn_CloudFrontFsd_Page.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_CloudFrontFsd_Page {
5
+ // called from plugin-admin
6
+ static public function admin_print_scripts_w3tc_cdn() {
7
+ wp_enqueue_script( 'w3tc_cdn_cloudfront_fsd',
8
+ plugins_url( 'Cdn_CloudFrontFsd_Page_View.js', W3TC_FILE ),
9
+ array( 'jquery' ), '1.0' );
10
+ }
11
+
12
+
13
+
14
+ static public function w3tc_settings_cdn() {
15
+ $config = Dispatcher::config();
16
+ include W3TC_DIR . '/Cdn_CloudFrontFsd_Page_View.php';
17
+ }
18
+ }
Cdn_CloudFrontFsd_Page_View.js ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tc_popup_resize(o) {
3
+ o.options.height = jQuery('.w3tc_popup_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+ $('body')
8
+ .on('click', '.w3tc_cdn_cloudfront_fsd_authorize', function() {
9
+ W3tc_Lightbox.open({
10
+ id:'w3tc-overlay',
11
+ close: '',
12
+ width: 800,
13
+ height: 300,
14
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
15
+ '&w3tc_action=cdn_cloudfront_fsd_intro',
16
+ callback: w3tc_popup_resize
17
+ });
18
+ })
19
+
20
+
21
+
22
+ .on('click', '.w3tc_cdn_cloudfront_fsd_list_distributions', function() {
23
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
24
+ '&w3tc_action=cdn_cloudfront_fsd_list_distributions';
25
+
26
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
27
+ var name = $(this).attr('name');
28
+ if (name)
29
+ url += '&' + encodeURIComponent(name) + '=' +
30
+ encodeURIComponent($(this).val());
31
+ });
32
+
33
+ W3tc_Lightbox.load(url, w3tc_popup_resize);
34
+ })
35
+
36
+
37
+
38
+ .on('click', '.w3tc_cdn_cloudfront_fsd_view_distribution', function() {
39
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
40
+ '&w3tc_action=cdn_cloudfront_fsd_view_distribution';
41
+
42
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
43
+ var name = $(this).attr('name');
44
+ var type = $(this).attr('type');
45
+ if (type == 'radio') {
46
+ if (!$(this).attr('checked'))
47
+ return;
48
+ }
49
+
50
+ if (name)
51
+ url += '&' + encodeURIComponent(name) + '=' +
52
+ encodeURIComponent($(this).val());
53
+ });
54
+
55
+ W3tc_Lightbox.load(url, w3tc_popup_resize);
56
+ })
57
+
58
+
59
+
60
+ .on('click', '.w3tc_cdn_cloudfront_fsd_configure_distribution', function() {
61
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
62
+ '&w3tc_action=cdn_cloudfront_fsd_configure_distribution';
63
+
64
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
65
+ var name = $(this).attr('name');
66
+ if (name)
67
+ url += '&' + encodeURIComponent(name) + '=' +
68
+ encodeURIComponent($(this).val());
69
+ });
70
+
71
+ W3tc_Lightbox.load(url, w3tc_popup_resize);
72
+ })
73
+
74
+
75
+
76
+ .on('click', '.w3tc_cdn_maxcdn_fsd_done', function() {
77
+ // refresh page
78
+ window.location = window.location + '&';
79
+ })
80
+
81
+
82
+
83
+ .on('size_change', '#cdn_cname_add', function() {
84
+ w3tc_popup_resize(W3tc_Lightbox);
85
+ })
86
+ });
Cdn_CloudFrontFsd_Page_View.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ $key = $config->get_string( 'cdn.cloudfront_fsd.access_key' );
8
+ $authorized = !empty( $key );
9
+
10
+ include W3TC_DIR . '/Cdn_Page_View_Header.php';
11
+ ?>
12
+ <p id="w3tc-options-menu">
13
+ <?php _e( 'Jump to:', 'w3-total-cache' ); ?>
14
+ <a href="?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a>
15
+ </p>
16
+ <form id="cdn_form" action="admin.php?page=w3tc_cdn" method="post">
17
+ <div class="metabox-holder">
18
+ <?php Util_Ui::postbox_header( __( 'Configuration', 'w3-total-cache' ),
19
+ '', 'configuration' ); ?>
20
+ <table class="form-table">
21
+ <tr>
22
+ <th style="width: 300px;">
23
+ <label>
24
+ <?php
25
+ _e( 'Specify account credentials:',
26
+ 'w3-total-cache' );
27
+ ?>
28
+ </label>
29
+ </th>
30
+ <td>
31
+ <?php if ( $authorized ): ?>
32
+ <input class="w3tc_cdn_cloudfront_fsd_authorize button-primary"
33
+ type="button"
34
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>"
35
+ />
36
+ <?php else: ?>
37
+ <input class="w3tc_cdn_cloudfront_fsd_authorize button-primary"
38
+ type="button"
39
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>"
40
+ />
41
+ <?php endif ?>
42
+ </td>
43
+ </tr>
44
+
45
+ <?php if ( $authorized ): ?>
46
+ <tr>
47
+ <th>
48
+ <label><?php _e( 'CDN CNAME:', 'w3-total-cache' ); ?></label>
49
+ </th>
50
+ <td class="w3tc_config_value_text">
51
+ <?php
52
+ echo $config->get_string( 'cdn.cloudfront_fsd.distribution_domain' )
53
+ ?><br />
54
+ <span class="description">
55
+ This website domain has to be CNAME pointing to this
56
+ CDN domain
57
+ </span>
58
+ </td>
59
+ </tr>
60
+ <?php endif ?>
61
+ </table>
62
+
63
+ <?php Util_Ui::button_config_save( 'cdn_configuration' ); ?>
64
+ <?php Util_Ui::postbox_footer(); ?>
65
+ </div>
66
+ </form>
67
+
68
+ <?php
69
+ include W3TC_INC_DIR . '/options/common/footer.php';
Cdn_CloudFrontFsd_Popup.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_CloudFrontFsd_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Cdn_CloudFrontFsd_Popup();
9
+
10
+ add_action( 'w3tc_ajax_cdn_cloudfront_fsd_intro',
11
+ array( $o, 'w3tc_ajax_cdn_cloudfront_fsd_intro' ) );
12
+ add_action( 'w3tc_ajax_cdn_cloudfront_fsd_list_distributions',
13
+ array( $o, 'w3tc_ajax_cdn_cloudfront_fsd_list_distributions' ) );
14
+ add_action( 'w3tc_ajax_cdn_cloudfront_fsd_view_distribution',
15
+ array( $o, 'w3tc_ajax_cdn_cloudfront_fsd_view_distribution' ) );
16
+ add_action( 'w3tc_ajax_cdn_cloudfront_fsd_configure_distribution',
17
+ array( $o, 'w3tc_ajax_cdn_cloudfront_fsd_configure_distribution' ) );
18
+ }
19
+
20
+
21
+
22
+ public function w3tc_ajax_cdn_cloudfront_fsd_intro() {
23
+ $this->render_intro( array() );
24
+ }
25
+
26
+
27
+
28
+ private function render_intro( $details ) {
29
+ $config = Dispatcher::config();
30
+ $url_obtain_key = Util_Ui::url( array(
31
+ 'page' => 'w3tc_dashboard',
32
+ 'w3tc_cdn_maxcdn_authorize' => 'y'
33
+ ) );
34
+
35
+ include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Intro.php';
36
+ exit();
37
+ }
38
+
39
+
40
+
41
+ public function w3tc_ajax_cdn_cloudfront_fsd_list_distributions() {
42
+ $access_key = $_REQUEST['access_key'];
43
+ $secret_key = $_REQUEST['secret_key'];
44
+
45
+ $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key );
46
+ if ( empty( $access_key ) || empty( $secret_key ) ) {
47
+ $this->render_intro( array(
48
+ 'error_message' => 'Can\'t authenticate: Access Key or Secret not valid'
49
+ ) );
50
+ exit();
51
+ }
52
+
53
+ try {
54
+ $distributions = $api->distributions_list();
55
+ } catch ( \Exception $ex ) {
56
+ $error_message = 'Can\'t authenticate: ' . $ex->getMessage();
57
+
58
+ $this->render_intro( array(
59
+ 'error_message' => $error_message
60
+ ) );
61
+ exit();
62
+ }
63
+
64
+ $items = array();
65
+
66
+ if ( isset( $distributions['Items']['DistributionSummary'] ) ) {
67
+ foreach ( $distributions['Items']['DistributionSummary'] as $i ) {
68
+ if ( empty( $i['Comment'] ) )
69
+ $i['Comment'] = $i['DomainName'];
70
+ if ( isset( $i['Origins']['Items']['Origin'] ) )
71
+ $i['Origin_DomainName'] = $i['Origins']['Items']['Origin'][0]['DomainName'];
72
+
73
+ $items[] = $i;
74
+ }
75
+ }
76
+
77
+ $details = array(
78
+ 'access_key' => $access_key,
79
+ 'secret_key' => $secret_key,
80
+ 'distributions' => $items
81
+ );
82
+
83
+ include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Distributions.php';
84
+ exit();
85
+ }
86
+
87
+
88
+
89
+ public function w3tc_ajax_cdn_cloudfront_fsd_view_distribution() {
90
+ $access_key = $_REQUEST['access_key'];
91
+ $secret_key = $_REQUEST['secret_key'];
92
+ $distribution_id = Util_Request::get( 'distribution_id', '' );
93
+
94
+ $details = array(
95
+ 'access_key' => $access_key,
96
+ 'secret_key' => $secret_key,
97
+ 'distribution_id' => $distribution_id,
98
+ 'distribution_comment' => '',
99
+ 'origin' => array(
100
+ 'new' => ''
101
+ ),
102
+ 'forward_querystring' => array(
103
+ 'new' => true
104
+ ),
105
+ 'forward_cookies' => array(
106
+ 'new' => true
107
+ ),
108
+ 'forward_host' => array(
109
+ 'new' => true
110
+ ),
111
+ 'alias' => array(
112
+ 'new' => Util_Environment::home_url_host()
113
+ )
114
+ );
115
+
116
+ if ( empty( $distribution_id ) ) {
117
+ // create new zone mode
118
+ $details['distribution_comment'] = Util_Request::get( 'comment_new' );
119
+ } else {
120
+ $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key );
121
+
122
+ try {
123
+ $distribution = $api->distribution_get( $distribution_id );
124
+ } catch ( \Exception $ex ) {
125
+ $this->render_intro( array(
126
+ 'error_message' => 'Can\'t obtain zone: ' . $ex->getMessage()
127
+ ) );
128
+ exit();
129
+ }
130
+
131
+ if ( isset( $distribution['DistributionConfig'] ) )
132
+ $c = $distribution['DistributionConfig'];
133
+ else
134
+ $c = array();
135
+
136
+ if ( !empty( $c['Comment'] ) )
137
+ $details['distribution_comment'] = $c['Comment'];
138
+ else
139
+ $details['distribution_comment'] = $c['DomainName'];
140
+
141
+ if ( isset( $c['Origins']['Items']['Origin'] ) ) {
142
+ $details['origin']['current'] =
143
+ $c['Origins']['Items']['Origin'][0]['DomainName'];
144
+ $details['origin']['new'] = $details['origin']['current'];
145
+ }
146
+
147
+ if ( isset( $c['DefaultCacheBehavior'] ) &&
148
+ isset( $c['DefaultCacheBehavior']['ForwardedValues'] ) )
149
+ $b = $c['DefaultCacheBehavior']['ForwardedValues'];
150
+ else
151
+ $b = array();
152
+
153
+ $details['forward_querystring']['current'] =
154
+ ( isset( $b['QueryString'] ) && $b['QueryString'] == 'true' );
155
+ $details['forward_cookies']['current'] =
156
+ ( isset( $b['Cookies'] ) && isset( $b['Cookies']['Forward'] ) &&
157
+ $b['Cookies']['Forward'] == 'all' );
158
+
159
+ if ( isset( $c['Aliases']['Items']['CNAME'][0] ) )
160
+ $details['alias']['current'] = $c['Aliases']['Items']['CNAME'][0];
161
+
162
+ $details['forward_host']['current'] = false;
163
+ if ( isset( $b['Headers']['Items']['Name'] ) ) {
164
+ foreach ( $b['Headers']['Items']['Name'] as $name )
165
+ if ( $name == 'Host' )
166
+ $details['forward_host']['current'] = true;
167
+ }
168
+ }
169
+
170
+
171
+
172
+ include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Distribution.php';
173
+ exit();
174
+ }
175
+
176
+
177
+
178
+ private function render_zone_value_change( $details, $field ) {
179
+ Util_Ui::hidden( '', $field, $details[$field]['new'] );
180
+
181
+ if ( !isset( $details[$field]['current'] ) ||
182
+ $details[$field]['current'] == $details[$field]['new'] )
183
+ echo htmlspecialchars( $details[$field]['new'] );
184
+ else {
185
+ echo 'currently set to <strong>' .
186
+ htmlspecialchars( empty( $details[$field]['current'] ) ?
187
+ '<empty>' : $details[$field]['current'] ) .
188
+ '</strong><br />';
189
+ echo 'will be changed to <strong>' .
190
+ htmlspecialchars( $details[$field]['new'] ) . '</strong><br />';
191
+ }
192
+ }
193
+
194
+
195
+
196
+ private function render_zone_boolean_change( $details, $field ) {
197
+ Util_Ui::hidden( '', $field, $details[$field]['new'] );
198
+
199
+ if ( !isset( $details[$field]['current'] ) ) {
200
+ echo 'will be set to <strong>';
201
+ echo $this->render_zone_boolean( $details[$field]['new'] );
202
+ echo '</strong>';
203
+ } else if ( $details[$field]['current'] == $details[$field]['new'] ) {
204
+ echo '<strong>';
205
+ echo $this->render_zone_boolean( $details[$field]['new'] );
206
+ echo '</strong>';
207
+ } else {
208
+ echo 'currently set to <strong>';
209
+ $this->render_zone_boolean( $details[$field]['current'] );
210
+ echo '</strong><br />';
211
+ echo 'will be changed to <strong>';
212
+ $this->render_zone_boolean( $details[$field]['new'] );
213
+ echo '</strong><br />';
214
+ }
215
+ }
216
+
217
+
218
+
219
+ private function render_zone_boolean( $v ) {
220
+ if ( $v == 0 )
221
+ echo 'disabled';
222
+ else
223
+ echo 'enabled';
224
+ }
225
+
226
+
227
+
228
+ private function render_zone_ip_change( $details, $field ) {
229
+ Util_Ui::textbox( '', $field, $details[$field]['new'] );
230
+
231
+ if ( isset( $details[$field]['current'] ) &&
232
+ $details[$field]['current'] != $details[$field]['new'] ) {
233
+ echo '<br /><span class="description">currently set to <strong>' .
234
+ $details[$field]['current'] . '</strong></span>';
235
+ }
236
+ }
237
+
238
+
239
+
240
+ public function w3tc_ajax_cdn_cloudfront_fsd_configure_distribution() {
241
+ $access_key = $_REQUEST['access_key'];
242
+ $secret_key = $_REQUEST['secret_key'];
243
+ $distribution_id = Util_Request::get( 'distribution_id', '' );
244
+
245
+ $origin_id = rand();
246
+
247
+ $distribution = array(
248
+ 'Comment' => Util_Request::get( 'distribution_comment' ),
249
+ 'Origins' => array(
250
+ 'Quantity' => 1,
251
+ 'Items' => array(
252
+ 'Origin' => array(
253
+ 'Id' => $origin_id,
254
+ 'DomainName' => Util_Request::get( 'origin' ),
255
+ 'OriginPath' => '',
256
+ 'CustomOriginConfig' => array(
257
+ 'HTTPPort' => 80,
258
+ 'HTTPSPort' => 443,
259
+ 'OriginProtocolPolicy' => 'match-viewer'
260
+ )
261
+ )
262
+ )
263
+ ),
264
+ 'Aliases' => array(
265
+ 'Quantity' => 1,
266
+ 'Items' => array(
267
+ 'CNAME' => Util_Request::get( 'alias' )
268
+ )
269
+ ),
270
+ 'DefaultCacheBehavior' => array(
271
+ 'TargetOriginId' => $origin_id,
272
+ 'ForwardedValues' => array(
273
+ 'QueryString' => 'true',
274
+ 'Cookies' => array(
275
+ 'Forward' => 'all'
276
+ ),
277
+ 'Headers' => array(
278
+ 'Quantity' => 1,
279
+ 'Items' => array(
280
+ 'Name' => 'Host'
281
+ )
282
+ )
283
+ ),
284
+ 'AllowedMethods' => array(
285
+ 'Quantity' => 7,
286
+ 'Items' => array(
287
+ 'Method' => array(
288
+ 'GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'PATCH',
289
+ 'DELETE'
290
+ )
291
+ ),
292
+ 'CachedMethods' => array(
293
+ 'Quantity' => 2,
294
+ 'Items' => array(
295
+ 'Method' => array(
296
+ 'GET', 'HEAD'
297
+ )
298
+ )
299
+ )
300
+ ),
301
+ 'MinTTL' => 0,
302
+ )
303
+ );
304
+
305
+ try {
306
+ $api = new Cdn_CloudFrontFsd_Api( $access_key, $secret_key );
307
+ if ( empty( $distribution_id ) ) {
308
+ $distribution['DefaultCacheBehavior']['TrustedSigners'] = array(
309
+ 'Enabled' => 'false',
310
+ 'Quantity' => 0
311
+ );
312
+ $distribution['DefaultCacheBehavior']['ViewerProtocolPolicy'] =
313
+ 'allow-all';
314
+
315
+ $response = $api->distribution_create( $distribution );
316
+ $distribution_id = $response['Id'];
317
+ } else {
318
+ $response = $api->distribution_update( $distribution_id, $distribution );
319
+ }
320
+ } catch ( \Exception $ex ) {
321
+ $this->render_intro( array(
322
+ 'error_message' => 'Failed to configure distribution: ' . $ex->getMessage()
323
+ ) );
324
+ exit();
325
+ }
326
+
327
+ $distribution_domain = $response['DomainName'];
328
+
329
+ $c = Dispatcher::config();
330
+ $c->set( 'cdn.cloudfront_fsd.access_key', $access_key );
331
+ $c->set( 'cdn.cloudfront_fsd.secret_key', $secret_key );
332
+ $c->set( 'cdn.cloudfront_fsd.distribution_id', $distribution_id );
333
+ $c->set( 'cdn.cloudfront_fsd.distribution_domain', $distribution_domain );
334
+ $c->save();
335
+
336
+ $details = array(
337
+ 'name' => $distribution['Comment'],
338
+ 'home_domain' => Util_Environment::home_url_host(),
339
+ 'dns_cname_target' => $distribution_domain,
340
+ );
341
+
342
+ include W3TC_DIR . '/Cdn_CloudFrontFsd_Popup_View_Success.php';
343
+ exit();
344
+ }
345
+ }
Cdn_CloudFrontFsd_Popup_View_Distribution.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form" method="post">
8
+ <?php
9
+ Util_Ui::hidden( '', 'access_key', $details['access_key'] );
10
+ Util_Ui::hidden( '', 'secret_key', $details['secret_key'] );
11
+ Util_Ui::hidden( '', 'distribution_id', $details['distribution_id'] );
12
+ Util_Ui::hidden( '', 'distribution_comment', $details['distribution_comment'] );
13
+ ?>
14
+
15
+ <div class="metabox-holder">
16
+ <?php Util_Ui::postbox_header( __( 'Configure distribution', 'w3-total-cache' ) ); ?>
17
+ <table class="form-table">
18
+ <tr>
19
+ <th>Distribution:</th>
20
+ <td><?php echo $details['distribution_comment'] ?></td>
21
+ </tr>
22
+ <tr>
23
+ <th>Origin:</th>
24
+ <td><?php $this->render_zone_ip_change( $details, 'origin' ) ?><br />
25
+ <span class="description">
26
+ Create DNS record pointing to your WordPress host IP.
27
+ CloudFront will use this host to grab your content.
28
+ It can not be your real domain name, since you will
29
+ point it to CloudFront's IP. For example for myblog.com
30
+ create origin.myblog.com and point it to the same IP
31
+ as myblog.com
32
+ </span>
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <th>Alias Domain:</th>
37
+ <td><?php $this->render_zone_value_change( $details, 'alias' ) ?></td>
38
+ </tr>
39
+ <tr>
40
+ <th>Forward Cookies:</th>
41
+ <td><?php $this->render_zone_boolean_change( $details, 'forward_cookies' ) ?></td>
42
+ </tr>
43
+ <tr>
44
+ <th>Forward Query String:</th>
45
+ <td><?php $this->render_zone_boolean_change( $details, 'forward_querystring' ) ?></td>
46
+ </tr>
47
+ <tr>
48
+ <th>Forward Host Header:</th>
49
+ <td><?php $this->render_zone_boolean_change( $details, 'forward_host' ) ?></td>
50
+ </tr>
51
+ </table>
52
+
53
+ <p class="submit">
54
+ <input type="button"
55
+ class="w3tc_cdn_cloudfront_fsd_configure_distribution w3tc-button-save button-primary"
56
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
57
+ </p>
58
+ <?php Util_Ui::postbox_footer(); ?>
59
+ </div>
60
+ </form>
Cdn_CloudFrontFsd_Popup_View_Distributions.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form" method="post">
8
+ <?php
9
+ Util_Ui::hidden( '', 'access_key', $details['access_key'] );
10
+ Util_Ui::hidden( '', 'secret_key', $details['secret_key'] );
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header( __( 'Select distribution to use', 'w3-total-cache' ) ); ?>
14
+ <table class="form-table">
15
+ <tr>
16
+ <td>Distribution:</td>
17
+ <td>
18
+ <?php
19
+ if ( count( $details['distributions'] ) > 15 )
20
+ echo '<div style="width: 100%; height: 300px; overflow-y: scroll">';
21
+ ?>
22
+
23
+ <?php foreach ( $details['distributions'] as $distribution ): ?>
24
+ <label>
25
+ <input name="distribution_id" type="radio" class="w3tc-ignore-change"
26
+ value="<?php echo $distribution['Id'] ?>" />
27
+ <?php echo $distribution['Comment'] ?>
28
+ (origin <?php echo $distribution['Origin_DomainName'] ?>)
29
+ </label><br />
30
+ <?php endforeach ?>
31
+
32
+ <label>
33
+ <input name="distribution_id" type="radio" class="w3tc-ignore-change" value=""
34
+ />
35
+ Add new distribution
36
+ <input name="comment_new" type="text" class="w3tc-ignore-change" />
37
+ </label>
38
+
39
+ <?php
40
+ if ( count( $details['distributions'] ) > 15 )
41
+ echo '</div>';
42
+ ?>
43
+ </td>
44
+ </tr>
45
+ </table>
46
+
47
+ <p class="submit">
48
+ <input type="button"
49
+ class="w3tc_cdn_cloudfront_fsd_view_distribution w3tc-button-save button-primary"
50
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
51
+ </p>
52
+ <?php Util_Ui::postbox_footer(); ?>
53
+ </div>
54
+ </form>
Cdn_CloudFrontFsd_Popup_View_Intro.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form">
8
+ <?php
9
+ if ( isset( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header(
14
+ __( 'Your CloudFront Account credentials', 'w3-total-cache' ) ); ?>
15
+ <table class="form-table">
16
+ <tr>
17
+ <td>Access Key:</td>
18
+ <td>
19
+ <input name="access_key" type="text" class="w3tc-ignore-change"
20
+ style="width: 550px"
21
+ value="<?php echo $config->get_string( 'cdn.cloudfront_fsd.access_key' ) ?>" />
22
+ </td>
23
+ </tr>
24
+ <tr>
25
+ <td>Access Secret:</td>
26
+ <td>
27
+ <input name="secret_key" type="text" class="w3tc-ignore-change"
28
+ style="width: 550px"
29
+ value="<?php echo $config->get_string( 'cdn.cloudfront_fsd.secret_key' ) ?>" />
30
+ </td>
31
+ </tr>
32
+ </table>
33
+
34
+ <p class="submit">
35
+ <input type="button"
36
+ class="w3tc_cdn_cloudfront_fsd_list_distributions w3tc-button-save button-primary"
37
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
38
+ </p>
39
+ <?php Util_Ui::postbox_footer(); ?>
40
+ </div>
41
+ </form>
Cdn_CloudFrontFsd_Popup_View_Success.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form">
8
+ <div class="metabox-holder">
9
+ <?php Util_Ui::postbox_header(
10
+ __( 'Succeeded', 'w3-total-cache' ) ); ?>
11
+
12
+ <div style="text-align: center">
13
+ Pull Zone <?php echo $details['name'] ?> was successfully configured.<br />
14
+ Now you need to change DNS records of your domain
15
+ <strong><?php echo $details['home_domain'] ?></strong> and CNAME it to
16
+ <strong><?php echo $details['dns_cname_target'] ?></strong> to make caching work.
17
+
18
+ <p class="submit">
19
+ <input type="button"
20
+ class="w3tc_cdn_maxcdn_fsd_done w3tc-button-save button-primary"
21
+ value="<?php _e( 'Done', 'w3-total-cache' ); ?>" />
22
+ </p>
23
+ <?php Util_Ui::postbox_footer(); ?>
24
+ </div>
25
+ </form>
Cdn_ConfigLabels.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_ConfigLabels {
5
+ public function config_labels( $config_labels ) {
6
+ return array_merge( $config_labels, array(
7
+ 'cdn.enabled' => __( '<acronym title="Content Delivery Network">CDN</acronym>:', 'w3-total-cache' ),
8
+ 'cdn.engine' => __( '<acronym title="Content Delivery Network">CDN</acronym> Type:', 'w3-total-cache' ),
9
+ 'cdn.debug' => __( '<acronym title="Content Delivery Network">CDN</acronym>', 'w3-total-cache' ),
10
+ 'cdn.uploads.enable' => __( 'Host attachments', 'w3-total-cache' ),
11
+ 'cdn.includes.enable' => __( 'Host wp-includes/ files', 'w3-total-cache' ),
12
+ 'cdn.theme.enable' => __( 'Host theme files', 'w3-total-cache' ),
13
+ 'cdn.minify.enable' => __( 'Host minified <acronym title="Cascading Style Sheet">CSS</acronym> and <acronym title="JavaScript">JS</acronym> files', 'w3-total-cache' ),
14
+ 'cdn.custom.enable' => __( 'Host custom files', 'w3-total-cache' ),
15
+ 'cdn.force.rewrite' => __( 'Force over-writing of existing files', 'w3-total-cache' ),
16
+ 'cdn.import.external' => __( 'Import external media library attachments', 'w3-total-cache' ),
17
+ 'cdn.canonical_header' => __( 'Add canonical header', 'w3-total-cache' ),
18
+ 'cdn.reject.ssl' => __( 'Disable <acronym title="Content Delivery Network">CDN</acronym> on <acronym title="Secure Sockets Layer">SSL</acronym> pages', 'w3-total-cache' ),
19
+ 'cdn.reject.logged_roles' => __( 'Disable <acronym title="Content Delivery Network">CDN</acronym> for the following roles', 'w3-total-cache' ),
20
+ 'cdn.reject.uri' => __( 'Disable <acronym title="Content Delivery Network">CDN</acronym> on the following pages:', 'w3-total-cache' ),
21
+ 'cdn.autoupload.enabled' => __( 'Export changed files automatically', 'w3-total-cache' ),
22
+ 'cdn.autoupload.interval' => __( 'Auto upload interval:', 'w3-total-cache' ),
23
+ 'cdn.queue.interval' => __( 'Re-transfer cycle interval:', 'w3-total-cache' ),
24
+ 'cdn.queue.limit' => __( 'Re-transfer cycle limit:', 'w3-total-cache' ),
25
+ 'cdn.includes.files' => __( 'wp-includes file types to upload:', 'w3-total-cache' ),
26
+ 'cdn.theme.files' => __( 'Theme file types to upload:', 'w3-total-cache' ),
27
+ 'cdn.import.files' => __( 'File types to import:', 'w3-total-cache' ),
28
+ 'cdn.custom.files' => __( 'Custom file list:', 'w3-total-cache' ),
29
+ 'cdn.rscf.location' => __( 'Location:', 'w3-total-cache' ),
30
+ 'cdn.reject.ua' => __( 'Rejected user agents:', 'w3-total-cache' ),
31
+ 'cdn.reject.files' => __( 'Rejected files:', 'w3-total-cache' ),
32
+ ) );
33
+ }
34
+ }
Cdn_Core.php ADDED
@@ -0,0 +1,725 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Total Cache CDN Plugin
6
+ */
7
+
8
+
9
+
10
+ /**
11
+ * class Cdn_Core
12
+ */
13
+ class Cdn_Core {
14
+ /**
15
+ * Config
16
+ */
17
+ private $_config = null;
18
+
19
+ /**
20
+ * Runs plugin
21
+ */
22
+ function __construct() {
23
+ $this->_config = Dispatcher::config();
24
+ }
25
+
26
+ /**
27
+ * Adds file to queue
28
+ *
29
+ * @param string $local_path
30
+ * @param string $remote_path
31
+ * @param integer $command
32
+ * @param string $last_error
33
+ * @return integer
34
+ */
35
+ function queue_add( $local_path, $remote_path, $command, $last_error ) {
36
+ global $wpdb;
37
+
38
+ $table = $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE;
39
+ $rows = $wpdb->get_results( $wpdb->prepare(
40
+ 'SELECT id, command '.
41
+ "FROM $table " .
42
+ 'WHERE local_path = %s AND remote_path = %s',
43
+ $local_path, $remote_path ) );
44
+
45
+ $already_exists = false;
46
+ foreach ( $rows as $row ) {
47
+ if ( $row->command != $command )
48
+ $wpdb->query( $wpdb->prepare(
49
+ "DELETE FROM $table " .
50
+ 'WHERE id = %d', $row->id ) );
51
+ else
52
+ $already_exists = true;
53
+ }
54
+
55
+ if ( $already_exists )
56
+ return true;
57
+
58
+ // insert if not yet there
59
+ return $wpdb->query( $wpdb->prepare(
60
+ "INSERT INTO $table " .
61
+ '(local_path, remote_path, command, last_error, date) ' .
62
+ 'VALUES (%s, %s, %d, %s, NOW())',
63
+ $local_path, $remote_path, $command, $last_error ) );
64
+ }
65
+
66
+ /**
67
+ * Returns array of array('local_path' => '', 'remote_path' => '') for specified file
68
+ *
69
+ * @param string $file
70
+ * @return array
71
+ */
72
+ function get_files_for_upload( $file ) {
73
+ $files = array();
74
+ $upload_info = Util_Http::upload_info();
75
+
76
+ if ( $upload_info ) {
77
+ $file = $this->normalize_attachment_file( $file );
78
+
79
+ $local_file = $upload_info['basedir'] . '/' . $file;
80
+ $remote_file = ltrim( $upload_info['baseurlpath'] . $file, '/' );
81
+
82
+ $files[] = $this->build_file_descriptor( $local_file, $remote_file );
83
+ }
84
+
85
+ return $files;
86
+ }
87
+
88
+ /**
89
+ * Returns array of files from sizes array
90
+ *
91
+ * @param string $attached_file
92
+ * @param array $sizes
93
+ * @return array
94
+ */
95
+ function _get_sizes_files( $attached_file, $sizes ) {
96
+ $files = array();
97
+ $base_dir = Util_File::dirname( $attached_file );
98
+
99
+ foreach ( (array) $sizes as $size ) {
100
+ if ( isset( $size['file'] ) ) {
101
+ if ( $base_dir ) {
102
+ $file = $base_dir . '/' . $size['file'];
103
+ } else {
104
+ $file = $size['file'];
105
+ }
106
+
107
+ $files = array_merge( $files, $this->get_files_for_upload( $file ) );
108
+ }
109
+ }
110
+
111
+ return $files;
112
+ }
113
+
114
+ /**
115
+ * Returns attachment files by metadata
116
+ *
117
+ * @param array $metadata
118
+ * @return array
119
+ */
120
+ function get_metadata_files( $metadata ) {
121
+ $files = array();
122
+
123
+ if ( isset( $metadata['file'] ) && isset( $metadata['sizes'] ) ) {
124
+ $files = array_merge( $files, $this->_get_sizes_files( $metadata['file'], $metadata['sizes'] ) );
125
+ }
126
+
127
+ return $files;
128
+ }
129
+
130
+ /**
131
+ * Returns attachment files by attachment ID
132
+ *
133
+ * @param integer $attachment_id
134
+ * @return array
135
+ */
136
+ function get_attachment_files( $attachment_id ) {
137
+ $files = array();
138
+
139
+ /**
140
+ * Get attached file
141
+ */
142
+ $attached_file = get_post_meta( $attachment_id, '_wp_attached_file', true );
143
+
144
+ if ( $attached_file != '' ) {
145
+ $files = array_merge( $files, $this->get_files_for_upload( $attached_file ) );
146
+
147
+ /**
148
+ * Get backup sizes files
149
+ */
150
+ $attachment_backup_sizes = get_post_meta( $attachment_id, '_wp_attachment_backup_sizes', true );
151
+
152
+ if ( is_array( $attachment_backup_sizes ) ) {
153
+ $files = array_merge( $files, $this->_get_sizes_files( $attached_file, $attachment_backup_sizes ) );
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Get files from metadata
159
+ */
160
+ $attachment_metadata = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
161
+
162
+ if ( is_array( $attachment_metadata ) ) {
163
+ $files = array_merge( $files, $this->get_metadata_files( $attachment_metadata ) );
164
+ }
165
+
166
+ return $files;
167
+ }
168
+
169
+ /**
170
+ * Uploads files to CDN
171
+ *
172
+ * @param array $files
173
+ * @param boolean $queue_failed
174
+ * @param array $results
175
+ * @return boolean
176
+ */
177
+ function upload( $files, $queue_failed, &$results, $timeout_time = NULL ) {
178
+ $cdn = $this->get_cdn();
179
+ $force_rewrite = $this->_config->get_boolean( 'cdn.force.rewrite' );
180
+
181
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_upload' ) );
182
+
183
+ $engine = $this->_config->get_string( 'cdn.engine' );
184
+ $return = $cdn->upload( $files, $results, $force_rewrite, $timeout_time );
185
+
186
+ if ( !$return && $queue_failed ) {
187
+ foreach ( $results as $result ) {
188
+ if ( $result['result'] != W3TC_CDN_RESULT_OK ) {
189
+ $this->queue_add( $result['local_path'], $result['remote_path'], W3TC_CDN_COMMAND_UPLOAD, $result['error'] );
190
+ }
191
+ }
192
+ }
193
+
194
+ return $return;
195
+ }
196
+
197
+ /**
198
+ * Deletes files frrom CDN
199
+ *
200
+ * @param array $files
201
+ * @param boolean $queue_failed
202
+ * @param array $results
203
+ * @return boolean
204
+ */
205
+ function delete( $files, $queue_failed, &$results ) {
206
+ $cdn = $this->get_cdn();
207
+
208
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_delete' ) );
209
+
210
+ $return = $cdn->delete( $files, $results );
211
+
212
+ if ( !$return && $queue_failed ) {
213
+ foreach ( $results as $result ) {
214
+ if ( $result['result'] != W3TC_CDN_RESULT_OK ) {
215
+ $this->queue_add( $result['local_path'], $result['remote_path'], W3TC_CDN_COMMAND_DELETE, $result['error'] );
216
+ }
217
+ }
218
+ }
219
+
220
+ return $return;
221
+ }
222
+
223
+ /**
224
+ * Purges files from CDN
225
+ *
226
+ * @param array $files consisting of array('local_path'=>'', 'remote_path'=>'')
227
+ * @param boolean $queue_failed
228
+ * @param array $results
229
+ * @return boolean
230
+ */
231
+ function purge( $files, $queue_failed, &$results ) {
232
+ /**
233
+ * Purge varnish servers before mirror purging
234
+ */
235
+ if ( Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) && $this->_config->get_boolean( 'varnish.enabled' ) ) {
236
+ $varnish = Dispatcher::component( 'Varnish_Flush' );
237
+
238
+ foreach ( $files as $file ) {
239
+ $remote_path = $file['remote_path'];
240
+ $varnish->flush_url( network_site_url( $remote_path ) );
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Purge CDN
246
+ */
247
+ $cdn = $this->get_cdn();
248
+
249
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_purge' ) );
250
+
251
+ $return = $cdn->purge( $files, $results );
252
+
253
+ if ( !$return && $queue_failed ) {
254
+ foreach ( $results as $result ) {
255
+ if ( $result['result'] != W3TC_CDN_RESULT_OK ) {
256
+ $this->queue_add( $result['local_path'], $result['remote_path'], W3TC_CDN_COMMAND_PURGE, $result['error'] );
257
+ }
258
+ }
259
+ }
260
+
261
+ return $return;
262
+ }
263
+
264
+ /**
265
+ * Purge CDN completely
266
+ *
267
+ * @param unknown $results
268
+ * @return mixed
269
+ */
270
+ function purge_all( &$results ) {
271
+ /**
272
+ * Purge CDN
273
+ */
274
+ $cdn = $this->get_cdn();
275
+
276
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_purge' ) );
277
+
278
+ $return = $cdn->purge_all( $results );
279
+ return $return;
280
+ }
281
+
282
+ /**
283
+ * Queues file upload.
284
+ * Links wp_cron call to do that by the end of request processing
285
+ *
286
+ * @param string $url
287
+ * @return void
288
+ */
289
+ function queue_upload_url( $url ) {
290
+ $docroot_filename = Util_Environment::url_to_docroot_filename( $url );
291
+ $filename = Util_Environment::document_root() . '/' . $docroot_filename;
292
+
293
+ $a = parse_url( $url );
294
+ $uri = $a['path'];
295
+
296
+ $remote_file_name = $this->uri_to_cdn_uri( $uri );
297
+ $this->queue_add( $filename, $remote_file_name,
298
+ W3TC_CDN_COMMAND_UPLOAD, 'Pending' );
299
+ }
300
+
301
+ /**
302
+ * Normalizes attachment file
303
+ *
304
+ * @param string $file
305
+ * @return string
306
+ */
307
+ function normalize_attachment_file( $file ) {
308
+ $upload_info = Util_Http::upload_info();
309
+ if ( $upload_info ) {
310
+ $file = ltrim( str_replace( $upload_info['basedir'], '', $file ), '/\\' );
311
+ $matches = null;
312
+
313
+ if ( preg_match( '~(\d{4}/\d{2}/)?[^/]+$~', $file, $matches ) ) {
314
+ $file = $matches[0];
315
+ }
316
+ }
317
+
318
+ return $file;
319
+ }
320
+
321
+ /**
322
+ * Returns CDN object
323
+ */
324
+ function get_cdn() {
325
+ static $cdn = array();
326
+
327
+ if ( !isset( $cdn[0] ) ) {
328
+ $engine = $this->_config->get_string( 'cdn.engine' );
329
+ $compression = ( $this->_config->get_boolean( 'browsercache.enabled' ) && $this->_config->get_boolean( 'browsercache.html.compression' ) );
330
+
331
+ switch ( $engine ) {
332
+ case 'akamai':
333
+ $engine_config = array(
334
+ 'username' => $this->_config->get_string( 'cdn.akamai.username' ),
335
+ 'password' => $this->_config->get_string( 'cdn.akamai.password' ),
336
+ 'zone' => $this->_config->get_string( 'cdn.akamai.zone' ),
337
+ 'domain' => $this->_config->get_array( 'cdn.akamai.domain' ),
338
+ 'ssl' => $this->_config->get_string( 'cdn.akamai.ssl' ),
339
+ 'email_notification' => $this->_config->get_array( 'cdn.akamai.email_notification' ),
340
+ 'compression' => false
341
+ );
342
+ break;
343
+
344
+ case 'att':
345
+ $engine_config = array(
346
+ 'account' => $this->_config->get_string( 'cdn.att.account' ),
347
+ 'token' => $this->_config->get_string( 'cdn.att.token' ),
348
+ 'domain' => $this->_config->get_array( 'cdn.att.domain' ),
349
+ 'ssl' => $this->_config->get_string( 'cdn.att.ssl' ),
350
+ 'compression' => false
351
+ );
352
+ break;
353
+
354
+ case 'azure':
355
+ $engine_config = array(
356
+ 'user' => $this->_config->get_string( 'cdn.azure.user' ),
357
+ 'key' => $this->_config->get_string( 'cdn.azure.key' ),
358
+ 'container' => $this->_config->get_string( 'cdn.azure.container' ),
359
+ 'cname' => $this->_config->get_array( 'cdn.azure.cname' ),
360
+ 'ssl' => $this->_config->get_string( 'cdn.azure.ssl' ),
361
+ 'compression' => $compression
362
+ );
363
+ break;
364
+
365
+ case 'cf':
366
+ $engine_config = array(
367
+ 'key' => $this->_config->get_string( 'cdn.cf.key' ),
368
+ 'secret' => $this->_config->get_string( 'cdn.cf.secret' ),
369
+ 'bucket' => $this->_config->get_string( 'cdn.cf.bucket' ),
370
+ 'id' => $this->_config->get_string( 'cdn.cf.id' ),
371
+ 'cname' => $this->_config->get_array( 'cdn.cf.cname' ),
372
+ 'ssl' => $this->_config->get_string( 'cdn.cf.ssl' ),
373
+ 'compression' => $compression
374
+ );
375
+ break;
376
+
377
+ case 'cf2':
378
+ $engine_config = array(
379
+ 'key' => $this->_config->get_string( 'cdn.cf2.key' ),
380
+ 'secret' => $this->_config->get_string( 'cdn.cf2.secret' ),
381
+ 'id' => $this->_config->get_string( 'cdn.cf2.id' ),
382
+ 'cname' => $this->_config->get_array( 'cdn.cf2.cname' ),
383
+ 'ssl' => $this->_config->get_string( 'cdn.cf2.ssl' ),
384
+ 'compression' => false
385
+ );
386
+ break;
387
+
388
+ case 'cotendo':
389
+ $engine_config = array(
390
+ 'username' => $this->_config->get_string( 'cdn.cotendo.username' ),
391
+ 'password' => $this->_config->get_string( 'cdn.cotendo.password' ),
392
+ 'zones' => $this->_config->get_array( 'cdn.cotendo.zones' ),
393
+ 'domain' => $this->_config->get_array( 'cdn.cotendo.domain' ),
394
+ 'ssl' => $this->_config->get_string( 'cdn.cotendo.ssl' ),
395
+ 'compression' => false
396
+ );
397
+ break;
398
+
399
+ case 'edgecast':
400
+ $engine_config = array(
401
+ 'account' => $this->_config->get_string( 'cdn.edgecast.account' ),
402
+ 'token' => $this->_config->get_string( 'cdn.edgecast.token' ),
403
+ 'domain' => $this->_config->get_array( 'cdn.edgecast.domain' ),
404
+ 'ssl' => $this->_config->get_string( 'cdn.edgecast.ssl' ),
405
+ 'compression' => false
406
+ );
407
+ break;
408
+
409
+ case 'ftp':
410
+ $engine_config = array(
411
+ 'host' => $this->_config->get_string( 'cdn.ftp.host' ),
412
+ 'type' => $this->_config->get_string( 'cdn.ftp.type' ),
413
+ 'user' => $this->_config->get_string( 'cdn.ftp.user' ),
414
+ 'pass' => $this->_config->get_string( 'cdn.ftp.pass' ),
415
+ 'path' => $this->_config->get_string( 'cdn.ftp.path' ),
416
+ 'pasv' => $this->_config->get_boolean( 'cdn.ftp.pasv' ),
417
+ 'domain' => $this->_config->get_array( 'cdn.ftp.domain' ),
418
+ 'ssl' => $this->_config->get_string( 'cdn.ftp.ssl' ),
419
+ 'compression' => false,
420
+ 'docroot' => Util_Environment::document_root()
421
+ );
422
+ break;
423
+
424
+ case 'google_drive':
425
+ $state = Dispatcher::config_state();
426
+
427
+ $engine_config = array(
428
+ 'client_id' =>
429
+ $this->_config->get_string( 'cdn.google_drive.client_id' ),
430
+ 'access_token' =>
431
+ $state->get_string( 'cdn.google_drive.access_token' ),
432
+ 'refresh_token' =>
433
+ $this->_config->get_string( 'cdn.google_drive.refresh_token' ),
434
+ 'root_url' =>
435
+ $this->_config->get_string( 'cdn.google_drive.folder.url' ),
436
+ 'root_folder_id' =>
437
+ $this->_config->get_string( 'cdn.google_drive.folder.id' ),
438
+ 'new_access_token_callback' => array(
439
+ $this,
440
+ 'on_google_drive_new_access_token'
441
+ )
442
+ );
443
+ break;
444
+
445
+ case 'highwinds':
446
+ $state = Dispatcher::config_state();
447
+
448
+ $engine_config = array(
449
+ 'domains' =>
450
+ $this->_config->get_array( 'cdn.highwinds.host.domains' ),
451
+ 'ssl' =>
452
+ $this->_config->get_string( 'cdn.highwinds.ssl' ),
453
+ 'api_token' =>
454
+ $this->_config->get_string( 'cdn.highwinds.api_token' ),
455
+ 'account_hash' =>
456
+ $this->_config->get_string( 'cdn.highwinds.account_hash' ),
457
+ 'host_hash_code' =>
458
+ $this->_config->get_string( 'cdn.highwinds.host.hash_code' )
459
+ );
460
+ break;
461
+
462
+ case 'maxcdn':
463
+ $engine_config = array(
464
+ 'authorization_key' => $this->_config->get_string( 'cdn.maxcdn.authorization_key' ),
465
+ 'zone_id' => $this->_config->get_integer( 'cdn.maxcdn.zone_id' ),
466
+ 'domain' => $this->_config->get_array( 'cdn.maxcdn.domain' ),
467
+ 'ssl' => $this->_config->get_string( 'cdn.maxcdn.ssl' ),
468
+ 'compression' => false
469
+ );
470
+ break;
471
+
472
+ case 'mirror':
473
+ $engine_config = array(
474
+ 'domain' => $this->_config->get_array( 'cdn.mirror.domain' ),
475
+ 'ssl' => $this->_config->get_string( 'cdn.mirror.ssl' ),
476
+ 'compression' => false
477
+ );
478
+ break;
479
+
480
+ case 'netdna':
481
+ $engine_config = array(
482
+ 'authorization_key' => $this->_config->get_string( 'cdn.netdna.authorization_key' ),
483
+ 'zone_id' => $this->_config->get_integer( 'cdn.netdna.zone_id' ),
484
+ 'domain' => $this->_config->get_array( 'cdn.netdna.domain' ),
485
+ 'ssl' => $this->_config->get_string( 'cdn.netdna.ssl' ),
486
+ 'compression' => false
487
+ );
488
+ break;
489
+
490
+ case 'rackspace_cdn':
491
+ $state = Dispatcher::config_state();
492
+
493
+ $engine_config = array(
494
+ 'user_name' => $this->_config->get_string( 'cdn.rackspace_cdn.user_name' ),
495
+ 'api_key' => $this->_config->get_string( 'cdn.rackspace_cdn.api_key' ),
496
+ 'region' => $this->_config->get_string( 'cdn.rackspace_cdn.region' ),
497
+ 'service_access_url' => $this->_config->get_string( 'cdn.rackspace_cdn.service.access_url' ),
498
+ 'service_id' => $this->_config->get_string( 'cdn.rackspace_cdn.service.id' ),
499
+ 'service_protocol' => $this->_config->get_string( 'cdn.rackspace_cdn.service.protocol' ),
500
+ 'domains' => $this->_config->get_array( 'cdn.rackspace_cdn.domains' ),
501
+ 'access_state' =>
502
+ $state->get_string( 'cdn.rackspace_cdn.access_state' ),
503
+ 'new_access_state_callback' => array(
504
+ $this,
505
+ 'on_rackspace_cdn_new_access_state'
506
+ )
507
+
508
+ );
509
+ break;
510
+ case 'rscf':
511
+ $state = Dispatcher::config_state();
512
+
513
+ $engine_config = array(
514
+ 'user_name' => $this->_config->get_string( 'cdn.rscf.user' ),
515
+ 'api_key' => $this->_config->get_string( 'cdn.rscf.key' ),
516
+ 'region' => $this->_config->get_string( 'cdn.rscf.location' ),
517
+ 'container' => $this->_config->get_string( 'cdn.rscf.container' ),
518
+ 'cname' => $this->_config->get_array( 'cdn.rscf.cname' ),
519
+ 'ssl' => $this->_config->get_string( 'cdn.rscf.ssl' ),
520
+ 'compression' => false,
521
+ 'access_state' =>
522
+ $state->get_string( 'cdn.rackspace_cf.access_state' ),
523
+ 'new_access_state_callback' => array(
524
+ $this,
525
+ 'on_rackspace_cf_new_access_state'
526
+ )
527
+
528
+ );
529
+ break;
530
+
531
+ case 's3':
532
+ $engine_config = array(
533
+ 'key' => $this->_config->get_string( 'cdn.s3.key' ),
534
+ 'secret' => $this->_config->get_string( 'cdn.s3.secret' ),
535
+ 'bucket' => $this->_config->get_string( 'cdn.s3.bucket' ),
536
+ 'cname' => $this->_config->get_array( 'cdn.s3.cname' ),
537
+ 'ssl' => $this->_config->get_string( 'cdn.s3.ssl' ),
538
+ 'compression' => $compression
539
+ );
540
+ break;
541
+
542
+ case 's3_compatible':
543
+ $engine_config = array(
544
+ 'key' => $this->_config->get_string( 'cdn.s3.key' ),
545
+ 'secret' => $this->_config->get_string( 'cdn.s3.secret' ),
546
+ 'bucket' => $this->_config->get_string( 'cdn.s3.bucket' ),
547
+ 'cname' => $this->_config->get_array( 'cdn.s3.cname' ),
548
+ 'ssl' => $this->_config->get_string( 'cdn.s3.ssl' ),
549
+ 'compression' => $compression,
550
+ 'api_host' => $this->_config->get_string( 'cdn.s3_compatible.api_host' )
551
+ );
552
+ break;
553
+ }
554
+
555
+ $engine_config = array_merge( $engine_config, array(
556
+ 'debug' => $this->_config->get_boolean( 'cdn.debug' )
557
+ ) );
558
+
559
+ $cdn[0] = CdnEngine::instance( $engine, $engine_config );
560
+
561
+ /**
562
+ * Set cache config for CDN
563
+ */
564
+ if ( $this->_config->get_boolean( 'browsercache.enabled' ) ) {
565
+ $w3_plugin_browsercache = Dispatcher::component( 'BrowserCache_Plugin' );
566
+ $cdn[0]->cache_config = $w3_plugin_browsercache->get_cache_config();
567
+ }
568
+ }
569
+
570
+ return $cdn[0];
571
+ }
572
+
573
+ /**
574
+ * Called when new access token is issued by cdnengine
575
+ */
576
+ public function on_google_drive_new_access_token( $access_token ) {
577
+ $state = Dispatcher::config_state();
578
+ $state->set( 'cdn.google_drive.access_token', $access_token );
579
+ $state->save();
580
+ }
581
+
582
+ /**
583
+ * Called when new access state is issued by cdnengine
584
+ */
585
+ public function on_rackspace_cdn_new_access_state( $access_state ) {
586
+ $state = Dispatcher::config_state();
587
+ $state->set( 'cdn.rackspace_cdn.access_state', $access_state );
588
+ $state->save();
589
+ }
590
+
591
+ /**
592
+ * Called when new access state is issued by cdnengine
593
+ */
594
+ public function on_rackspace_cf_new_access_state( $access_state ) {
595
+ $state = Dispatcher::config_state();
596
+ $state->set( 'cdn.rackspace_cf.access_state', $access_state );
597
+ $state->save();
598
+ }
599
+
600
+ /**
601
+ * Convert relative file which is relative to ABSPATH (wp folder on disc) to path uri
602
+ *
603
+ * @param unknown $file
604
+ * @return string
605
+ */
606
+ function docroot_filename_to_uri( $file ) {
607
+ $file = ltrim( $file, '/' );
608
+ // Translate multisite subsite uploads paths
609
+ $file = str_replace( basename( WP_CONTENT_DIR ) . '/blogs.dir/' .
610
+ Util_Environment::blog_id() . '/', '', $file );
611
+ return $file;
612
+
613
+ }
614
+
615
+ /**
616
+ * Convert a relative path (relative to ABSPATH (wp folder on disc) into a absolute path
617
+ *
618
+ * @param unknown $file
619
+ * @return string
620
+ */
621
+ function docroot_filename_to_absolute_path( $file ) {
622
+ if ( is_file( $file ) )
623
+ return $file;
624
+
625
+ return rtrim( Util_Environment::document_root(), "/" ) . '/' . ltrim( $file, "/" );
626
+ }
627
+
628
+ /**
629
+ * Convert local uri path to CDN type specific path
630
+ *
631
+ * @param unknown $local_uri_path
632
+ * @return string
633
+ */
634
+ function uri_to_cdn_uri( $local_uri ) {
635
+ $local_uri = ltrim( $local_uri, '/' );
636
+ $remote_uri = $local_uri;
637
+
638
+ if ( Util_Environment::is_wpmu() && defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING )
639
+ $remote_uri = str_replace( site_url(), '', $local_uri );
640
+
641
+ $engine = $this->_config->get_string( 'cdn.engine' );
642
+
643
+ if ( Cdn_Util::is_engine_mirror( $engine ) ) {
644
+ if ( Util_Environment::is_wpmu() && strpos( $local_uri, 'files' ) === 0 ) {
645
+ $upload_dir = Util_Environment::wp_upload_dir();
646
+ $remote_uri = $this->abspath_to_relative_path(
647
+ dirname( $upload_dir['basedir'] ) ) . '/' . $local_uri;
648
+ }
649
+ }
650
+ elseif ( Util_Environment::is_wpmu() &&
651
+ !Util_Environment::is_wpmu_subdomain() &&
652
+ Util_Environment::is_using_master_config() &&
653
+ Cdn_Util::is_engine_push( $engine ) ) {
654
+ // in common config files are uploaded for network home url
655
+ // so mirror will not contain /subblog/ path in uri
656
+ $home = trim( home_url( '', 'relative' ), '/' ) . '/';
657
+ $network_home = trim( network_home_url( '', 'relative' ), '/' ) . '/';
658
+
659
+ if ( $home != $network_home &&
660
+ substr( $local_uri, 0, strlen( $home ) ) == $home ) {
661
+ $remote_uri = $network_home . substr( $local_uri, strlen( $home ) );
662
+ }
663
+ }
664
+
665
+ return ltrim( $remote_uri, '/' );
666
+ }
667
+
668
+ /**
669
+ * Returns the sitepath for multisite subfolder or subdomain path for multisite subdomain
670
+ *
671
+ * @return string
672
+ */
673
+ private function _get_multisite_url_identifier() {
674
+ if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
675
+ $parsedUrl = parse_url( site_url() );
676
+ return $parsedUrl['host'];
677
+ } elseif ( Util_Environment::is_wpmu_subdomain() ) {
678
+ $parsedUrl = parse_url( Util_Environment::home_domain_root_url() );
679
+ $urlparts = explode( '.', $parsedUrl['host'] );
680
+
681
+ if ( sizeof( $urlparts ) > 2 ) {
682
+ $subdomain = array_shift( $urlparts );
683
+ return trim( $subdomain, '/' );
684
+ }
685
+ }
686
+ return trim( Util_Environment::site_url_uri(), '/' );
687
+ }
688
+
689
+ /**
690
+ * Taks an absolute path and converts to a relative path to root
691
+ *
692
+ * @param unknown $path
693
+ * @return mixed
694
+ */
695
+ function abspath_to_relative_path( $path ) {
696
+ return str_replace( Util_Environment::document_root(), '', $path );
697
+ }
698
+
699
+ /**
700
+ * Takes a root relative path and converts to a full uri
701
+ *
702
+ * @param unknown $path
703
+ * @return string
704
+ */
705
+ function relative_path_to_url( $path ) {
706
+ $cdnuri = $this->docroot_filename_to_uri( ltrim( $path, "/" ) );
707
+ return rtrim( Util_Environment::home_domain_root_url(), "/" ) . '/' . $cdnuri;
708
+ }
709
+
710
+ /**
711
+ * Constructs a CDN file descriptor
712
+ *
713
+ * @param unknown $local_path
714
+ * @param unknown $remote_path
715
+ * @return array
716
+ */
717
+ function build_file_descriptor( $local_path, $remote_path ) {
718
+ $file = array( 'local_path' => $local_path,
719
+ 'remote_path' => $remote_path,
720
+ 'original_url' => $this->relative_path_to_url( $local_path ) );
721
+
722
+ $file = apply_filters( 'w3tc_build_cdn_file_array', $file );
723
+ return $file;
724
+ }
725
+ }
Cdn_Core_Admin.php ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Total Cache CDN Plugin
6
+ */
7
+
8
+
9
+
10
+
11
+ /**
12
+ * class Cdn_Core_Admin
13
+ */
14
+ class Cdn_Core_Admin {
15
+ /**
16
+ * Config
17
+ */
18
+ private $_config = null;
19
+
20
+ /**
21
+ * Runs plugin
22
+ */
23
+ function __construct() {
24
+ $this->_config = Dispatcher::config();
25
+ }
26
+
27
+ /**
28
+ * Purge attachment
29
+ *
30
+ * Upload _wp_attached_file, _wp_attachment_metadata, _wp_attachment_backup_sizes
31
+ *
32
+ * @param integer $attachment_id
33
+ * @param array $results
34
+ * @return boolean
35
+ */
36
+ function purge_attachment( $attachment_id, &$results ) {
37
+ $common = Dispatcher::component( 'Cdn_Core' );
38
+ $files = $common->get_attachment_files( $attachment_id );
39
+
40
+ return $common->purge( $files, false, $results );
41
+ }
42
+
43
+ /**
44
+ * Updates file date in the queue
45
+ *
46
+ * @param integer $queue_id
47
+ * @param string $last_error
48
+ * @return integer
49
+ */
50
+ function queue_update( $queue_id, $last_error ) {
51
+ global $wpdb;
52
+
53
+ $sql = sprintf( 'UPDATE %s SET last_error = "%s", date = NOW() WHERE id = %d', $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE, esc_sql( $last_error ), $queue_id );
54
+
55
+ return $wpdb->query( $sql );
56
+ }
57
+
58
+ /**
59
+ * Removes from queue
60
+ *
61
+ * @param integer $queue_id
62
+ * @return integer
63
+ */
64
+ function queue_delete( $queue_id ) {
65
+ global $wpdb;
66
+
67
+ $sql = sprintf( 'DELETE FROM %s WHERE id = %d', $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE, $queue_id );
68
+
69
+ return $wpdb->query( $sql );
70
+ }
71
+
72
+ /**
73
+ * Empties queue
74
+ *
75
+ * @param integer $command
76
+ * @return integer
77
+ */
78
+ function queue_empty( $command ) {
79
+ global $wpdb;
80
+
81
+ $sql = sprintf( 'DELETE FROM %s WHERE command = %d', $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE, $command );
82
+
83
+ return $wpdb->query( $sql );
84
+ }
85
+
86
+ /**
87
+ * Returns queue
88
+ *
89
+ * @param integer $limit
90
+ * @return array
91
+ */
92
+ function queue_get( $limit = null ) {
93
+ global $wpdb;
94
+
95
+ $sql = sprintf( 'SELECT * FROM %s%s ORDER BY date', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
96
+
97
+ if ( $limit ) {
98
+ $sql .= sprintf( ' LIMIT %d', $limit );
99
+ }
100
+
101
+ $results = $wpdb->get_results( $sql );
102
+ $queue = array();
103
+
104
+ if ( $results ) {
105
+ foreach ( (array) $results as $result ) {
106
+ $queue[$result->command][] = $result;
107
+ }
108
+ }
109
+
110
+ return $queue;
111
+ }
112
+
113
+ /**
114
+ * Process queue
115
+ *
116
+ * @param integer $limit
117
+ * @return integer
118
+ */
119
+ function queue_process( $limit ) {
120
+ $items = 0;
121
+
122
+ $commands = $this->queue_get( $limit );
123
+ $force_rewrite = $this->_config->get_boolean( 'cdn.force.rewrite' );
124
+
125
+ if ( count( $commands ) ) {
126
+ $common = Dispatcher::component( 'Cdn_Core' );
127
+ $cdn = $common->get_cdn();
128
+
129
+ foreach ( $commands as $command => $queue ) {
130
+ $files = array();
131
+ $results = array();
132
+ $map = array();
133
+
134
+ foreach ( $queue as $result ) {
135
+ $files[] = $common->build_file_descriptor( $result->local_path, $result->remote_path );
136
+ $map[$result->local_path] = $result->id;
137
+ $items++;
138
+ }
139
+
140
+ switch ( $command ) {
141
+ case W3TC_CDN_COMMAND_UPLOAD:
142
+ foreach ( $files as $file ) {
143
+ $local_file_name = $file['local_path'];
144
+ $remote_file_name = $file['remote_path'];
145
+ if ( !file_exists( $local_file_name ) ) {
146
+ Dispatcher::create_file_for_cdn( $local_file_name );
147
+ }
148
+ }
149
+
150
+ $cdn->upload( $files, $results, $force_rewrite );
151
+
152
+ foreach ( $results as $result ) {
153
+ if ( $result['result'] == W3TC_CDN_RESULT_OK ) {
154
+ Dispatcher::on_cdn_file_upload( $result['local_path'] );
155
+ }
156
+ }
157
+ break;
158
+
159
+ case W3TC_CDN_COMMAND_DELETE:
160
+ $cdn->delete( $files, $results );
161
+ break;
162
+
163
+ case W3TC_CDN_COMMAND_PURGE:
164
+ $cdn->purge( $files, $results );
165
+ break;
166
+ }
167
+
168
+ foreach ( $results as $result ) {
169
+ if ( $result['result'] == W3TC_CDN_RESULT_OK ) {
170
+ $this->queue_delete( $map[$result['local_path']] );
171
+ } else {
172
+ $this->queue_update( $map[$result['local_path']], $result['error'] );
173
+ }
174
+ }
175
+ }
176
+ }
177
+
178
+ return $items;
179
+ }
180
+
181
+ /**
182
+ * Export library to CDN
183
+ *
184
+ * @param integer $limit
185
+ * @param integer $offset
186
+ * @param integer $count
187
+ * @param integer $total
188
+ * @param array $results
189
+ * @return void
190
+ */
191
+ function export_library( $limit, $offset, &$count, &$total, &$results, $timeout_time = 0 ) {
192
+ global $wpdb;
193
+
194
+ $count = 0;
195
+ $total = 0;
196
+
197
+ $upload_info = Util_Http::upload_info();
198
+
199
+ if ( $upload_info ) {
200
+ $sql = sprintf( 'SELECT
201
+ pm.meta_value AS file,
202
+ pm2.meta_value AS metadata
203
+ FROM
204
+ %sposts AS p
205
+ LEFT JOIN
206
+ %spostmeta AS pm ON p.ID = pm.post_ID AND pm.meta_key = "_wp_attached_file"
207
+ LEFT JOIN
208
+ %spostmeta AS pm2 ON p.ID = pm2.post_ID AND pm2.meta_key = "_wp_attachment_metadata"
209
+ WHERE
210
+ p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL)
211
+ GROUP BY
212
+ p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix );
213
+
214
+ if ( $limit ) {
215
+ $sql .= sprintf( ' LIMIT %d', $limit );
216
+
217
+ if ( $offset ) {
218
+ $sql .= sprintf( ' OFFSET %d', $offset );
219
+ }
220
+ }
221
+
222
+ $posts = $wpdb->get_results( $sql );
223
+
224
+ if ( $posts ) {
225
+ $count = count( $posts );
226
+ $total = $this->get_attachments_count();
227
+ $files = array();
228
+
229
+ $common = Dispatcher::component( 'Cdn_Core' );
230
+
231
+ foreach ( $posts as $post ) {
232
+ $post_files = array();
233
+
234
+ if ( $post->file ) {
235
+ $file = $common->normalize_attachment_file( $post->file );
236
+
237
+ $local_file = $upload_info['basedir'] . '/' . $file;
238
+ $remote_file = ltrim( $upload_info['baseurlpath'] . $file, '/' );
239
+
240
+ $post_files[] = $common->build_file_descriptor( $local_file, $remote_file );
241
+ }
242
+
243
+ if ( $post->metadata ) {
244
+ $metadata = @unserialize( $post->metadata );
245
+
246
+ $post_files = array_merge( $post_files, $common->get_metadata_files( $metadata ) );
247
+ }
248
+
249
+ $post_files = apply_filters( 'w3tc_cdn_add_attachment', $post_files );
250
+
251
+ $files = array_merge( $files, $post_files );
252
+ }
253
+
254
+ $common = Dispatcher::component( 'Cdn_Core' );
255
+ $common->upload( $files, false, $results, $timeout_time );
256
+ }
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Imports library
262
+ *
263
+ * @param integer $limit
264
+ * @param integer $offset
265
+ * @param integer $count
266
+ * @param integer $total
267
+ * @param array $results
268
+ * @return boolean
269
+ */
270
+ function import_library( $limit, $offset, &$count, &$total, &$results ) {
271
+ global $wpdb;
272
+
273
+ $count = 0;
274
+ $total = 0;
275
+ $results = array();
276
+
277
+ $upload_info = Util_Http::upload_info();
278
+ $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' );
279
+ $document_root = Util_Environment::document_root();
280
+
281
+ @set_time_limit( $this->_config->get_integer( 'timelimit.cdn_import' ) );
282
+
283
+ if ( $upload_info ) {
284
+ /**
285
+ * Search for posts with links or images
286
+ */
287
+ $sql = sprintf( 'SELECT
288
+ ID,
289
+ post_content,
290
+ post_date
291
+ FROM
292
+ %sposts
293
+ WHERE
294
+ post_status = "publish"
295
+ AND (post_type = "post" OR post_type = "page")
296
+ AND (post_content LIKE "%%src=%%"
297
+ OR post_content LIKE "%%href=%%")
298
+ ', $wpdb->prefix );
299
+
300
+ if ( $limit ) {
301
+ $sql .= sprintf( ' LIMIT %d', $limit );
302
+
303
+ if ( $offset ) {
304
+ $sql .= sprintf( ' OFFSET %d', $offset );
305
+ }
306
+ }
307
+
308
+ $posts = $wpdb->get_results( $sql );
309
+
310
+ if ( $posts ) {
311
+ $count = count( $posts );
312
+ $total = $this->get_import_posts_count();
313
+ $regexp = '~(' . $this->get_regexp_by_mask( $this->_config->get_string( 'cdn.import.files' ) ) . ')$~';
314
+ $config_state = Dispatcher::config_state();
315
+ $import_external = $config_state->get_boolean( 'cdn.import.external' );
316
+
317
+ foreach ( $posts as $post ) {
318
+ $matches = null;
319
+ $replaced = array();
320
+ $attachments = array();
321
+ $post_content = $post->post_content;
322
+
323
+ /**
324
+ * Search for all link and image sources
325
+ */
326
+ if ( preg_match_all( '~(href|src)=[\'"]?([^\'"<>\s]+)[\'"]?~', $post_content, $matches, PREG_SET_ORDER ) ) {
327
+ foreach ( $matches as $match ) {
328
+ list( $search, $attribute, $origin ) = $match;
329
+
330
+ /**
331
+ * Check if $search is already replaced
332
+ */
333
+ if ( isset( $replaced[$search] ) ) {
334
+ continue;
335
+ }
336
+
337
+ $error = '';
338
+ $result = false;
339
+
340
+ $src = Util_Environment::normalize_file_minify( $origin );
341
+ $dst = '';
342
+
343
+ /**
344
+ * Check if file exists in the library
345
+ */
346
+ if ( stristr( $origin, $upload_info['baseurl'] ) === false ) {
347
+ /**
348
+ * Check file extension
349
+ */
350
+ $check_src = $src;
351
+
352
+ if ( Util_Environment::is_url( $check_src ) ) {
353
+ $qpos = strpos( $check_src, '?' );
354
+
355
+ if ( $qpos !== false ) {
356
+ $check_src = substr( $check_src, 0, $qpos );
357
+ }
358
+ }
359
+
360
+ if ( preg_match( $regexp, $check_src ) ) {
361
+ /**
362
+ * Check for already uploaded attachment
363
+ */
364
+ if ( isset( $attachments[$src] ) ) {
365
+ list( $dst, $dst_url ) = $attachments[$src];
366
+ $result = true;
367
+ } else {
368
+ if ( $uploads_use_yearmonth_folders ) {
369
+ $upload_subdir = date( 'Y/m', strtotime( $post->post_date ) );
370
+ $upload_dir = sprintf( '%s/%s', $upload_info['basedir'], $upload_subdir );
371
+ $upload_url = sprintf( '%s/%s', $upload_info['baseurl'], $upload_subdir );
372
+ } else {
373
+ $upload_subdir = '';
374
+ $upload_dir = $upload_info['basedir'];
375
+ $upload_url = $upload_info['baseurl'];
376
+ }
377
+
378
+ $src_filename = pathinfo( $src, PATHINFO_FILENAME );
379
+ $src_extension = pathinfo( $src, PATHINFO_EXTENSION );
380
+
381
+ /**
382
+ * Get available filename
383
+ */
384
+ for ( $i = 0; ; $i++ ) {
385
+ $dst = sprintf( '%s/%s%s%s', $upload_dir, $src_filename, ( $i ? $i : '' ), ( $src_extension ? '.' . $src_extension : '' ) );
386
+
387
+ if ( !file_exists( $dst ) ) {
388
+ break;
389
+ }
390
+ }
391
+
392
+ $dst_basename = basename( $dst );
393
+ $dst_url = sprintf( '%s/%s', $upload_url, $dst_basename );
394
+ $dst_path = ltrim( str_replace( $document_root, '', Util_Environment::normalize_path( $dst ) ), '/' );
395
+
396
+ if ( $upload_subdir ) {
397
+ Util_File::mkdir( $upload_subdir, 0777, $upload_info['basedir'] );
398
+ }
399
+
400
+ $download_result = false;
401
+
402
+ /**
403
+ * Check if file is remote URL
404
+ */
405
+ if ( Util_Environment::is_url( $src ) ) {
406
+ /**
407
+ * Download file
408
+ */
409
+ if ( $import_external ) {
410
+ $download_result = Util_Http::download( $src, $dst );
411
+
412
+ if ( !$download_result ) {
413
+ $error = 'Unable to download file';
414
+ }
415
+ } else {
416
+ $error = 'External file import is disabled';
417
+ }
418
+ } else {
419
+ /**
420
+ * Otherwise copy file from local path
421
+ */
422
+ $src_path = $document_root . '/' . urldecode( $src );
423
+
424
+ if ( file_exists( $src_path ) ) {
425
+ $download_result = @copy( $src_path, $dst );
426
+
427
+ if ( !$download_result ) {
428
+ $error = 'Unable to copy file';
429
+ }
430
+ } else {
431
+ $error = 'Source file doesn\'t exists';
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Check if download or copy was successful
437
+ */
438
+ if ( $download_result ) {
439
+
440
+
441
+ $title = $dst_basename;
442
+ $guid = ltrim( $upload_info['baseurlpath'] . $title, ',' );
443
+ $mime_type = Util_Mime::get_mime_type( $dst );
444
+
445
+ $GLOBALS['wp_rewrite'] = new WP_Rewrite();
446
+
447
+ /**
448
+ * Insert attachment
449
+ */
450
+ $id = wp_insert_attachment( array(
451
+ 'post_mime_type' => $mime_type,
452
+ 'guid' => $guid,
453
+ 'post_title' => $title,
454
+ 'post_content' => '',
455
+ 'post_parent' => $post->ID
456
+ ), $dst );
457
+
458
+ if ( !is_wp_error( $id ) ) {
459
+ /**
460
+ * Generate attachment metadata and upload to CDN
461
+ */
462
+ require_once ABSPATH . 'wp-admin/includes/image.php';
463
+ wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $dst ) );
464
+
465
+ $attachments[$src] = array(
466
+ $dst,
467
+ $dst_url
468
+ );
469
+
470
+ $result = true;
471
+ } else {
472
+ $error = 'Unable to insert attachment';
473
+ }
474
+ }
475
+ }
476
+
477
+ /**
478
+ * If attachment was successfully created then replace links
479
+ */
480
+ if ( $result ) {
481
+ $replace = sprintf( '%s="%s"', $attribute, $dst_url );
482
+
483
+ // replace $search with $replace
484
+ $post_content = str_replace( $search, $replace, $post_content );
485
+
486
+ $replaced[$search] = $replace;
487
+ $error = 'OK';
488
+ }
489
+ } else {
490
+ $error = 'File type rejected';
491
+ }
492
+ } else {
493
+ $error = 'File already exists in the media library';
494
+ }
495
+
496
+ /**
497
+ * Add new entry to the log file
498
+ */
499
+
500
+ $results[] = array(
501
+ 'src' => $src,
502
+ 'dst' => $dst_path,
503
+ 'result' => $result,
504
+ 'error' => $error
505
+ );
506
+ }
507
+ }
508
+
509
+ /**
510
+ * If post content was chenged then update DB
511
+ */
512
+ if ( $post_content != $post->post_content ) {
513
+ wp_update_post( array(
514
+ 'ID' => $post->ID,
515
+ 'post_content' => $post_content
516
+ ) );
517
+ }
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Rename domain
525
+ *
526
+ * @param array $names
527
+ * @param integer $limit
528
+ * @param integer $offset
529
+ * @param integer $count
530
+ * @param integer $total
531
+ * @param integer $results
532
+ * @return void
533
+ */
534
+ function rename_domain( $names, $limit, $offset, &$count, &$total, &$results ) {
535
+ global $wpdb;
536
+
537
+ @set_time_limit( $this->_config->get_integer( 'timelimit.domain_rename' ) );
538
+
539
+ $count = 0;
540
+ $total = 0;
541
+ $results = array();
542
+
543
+ $upload_info = Util_Http::upload_info();
544
+
545
+ foreach ( $names as $index => $name ) {
546
+ $names[$index] = str_ireplace( 'www.', '', $name );
547
+ }
548
+
549
+ if ( $upload_info ) {
550
+ $sql = sprintf( 'SELECT
551
+ ID,
552
+ post_content,
553
+ post_date
554
+ FROM
555
+ %sposts
556
+ WHERE
557
+ post_status = "publish"
558
+ AND (post_type = "post" OR post_type = "page")
559
+ AND (post_content LIKE "%%src=%%"
560
+ OR post_content LIKE "%%href=%%")
561
+ ', $wpdb->prefix );
562
+
563
+ if ( $limit ) {
564
+ $sql .= sprintf( ' LIMIT %d', $limit );
565
+
566
+ if ( $offset ) {
567
+ $sql .= sprintf( ' OFFSET %d', $offset );
568
+ }
569
+ }
570
+
571
+ $posts = $wpdb->get_results( $sql );
572
+
573
+ if ( $posts ) {
574
+ $count = count( $posts );
575
+ $total = $this->get_rename_posts_count();
576
+ $names_quoted = array_map( array( '\W3TC\Util_Environment', 'preg_quote' ), $names );
577
+
578
+ foreach ( $posts as $post ) {
579
+ $matches = null;
580
+ $post_content = $post->post_content;
581
+ $regexp = '~(href|src)=[\'"]?(https?://(www\.)?(' . implode( '|', $names_quoted ) . ')' . Util_Environment::preg_quote( $upload_info['baseurlpath'] ) . '([^\'"<>\s]+))[\'"]~';
582
+
583
+ if ( preg_match_all( $regexp, $post_content, $matches, PREG_SET_ORDER ) ) {
584
+ foreach ( $matches as $match ) {
585
+ $old_url = $match[2];
586
+ $new_url = sprintf( '%s/%s', $upload_info['baseurl'], $match[5] );
587
+ $post_content = str_replace( $old_url, $new_url, $post_content );
588
+
589
+ $results[] = array(
590
+ 'old' => $old_url,
591
+ 'new' => $new_url,
592
+ 'result' => true,
593
+ 'error' => 'OK'
594
+ );
595
+ }
596
+ }
597
+
598
+ if ( $post_content != $post->post_content ) {
599
+ wp_update_post( array(
600
+ 'ID' => $post->ID,
601
+ 'post_content' => $post_content
602
+ ) );
603
+ }
604
+ }
605
+ }
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Returns attachments count
611
+ *
612
+ * @return integer
613
+ */
614
+ function get_attachments_count() {
615
+ global $wpdb;
616
+
617
+ $sql = sprintf( 'SELECT COUNT(DISTINCT p.ID)
618
+ FROM %sposts AS p
619
+ LEFT JOIN %spostmeta AS pm ON p.ID = pm.post_ID
620
+ AND pm.meta_key = "_wp_attached_file"
621
+ LEFT JOIN %spostmeta AS pm2 ON p.ID = pm2.post_ID
622
+ AND pm2.meta_key = "_wp_attachment_metadata"
623
+ WHERE p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL)', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix );
624
+
625
+ return $wpdb->get_var( $sql );
626
+ }
627
+
628
+ /**
629
+ * Returns import posts count
630
+ *
631
+ * @return integer
632
+ */
633
+ function get_import_posts_count() {
634
+ global $wpdb;
635
+
636
+ $sql = sprintf( 'SELECT
637
+ COUNT(*)
638
+ FROM
639
+ %sposts
640
+ WHERE
641
+ post_status = "publish"
642
+ AND (post_type = "post" OR post_type = "page")
643
+ AND (post_content LIKE "%%src=%%"
644
+ OR post_content LIKE "%%href=%%")
645
+ ', $wpdb->prefix );
646
+
647
+ return $wpdb->get_var( $sql );
648
+ }
649
+
650
+ /**
651
+ * Returns rename posts count
652
+ *
653
+ * @return integer
654
+ */
655
+ function get_rename_posts_count() {
656
+ return $this->get_import_posts_count();
657
+ }
658
+
659
+ /**
660
+ * Returns regexp by mask
661
+ *
662
+ * @param string $mask
663
+ * @return string
664
+ */
665
+ function get_regexp_by_mask( $mask ) {
666
+ $mask = trim( $mask );
667
+ $mask = Util_Environment::preg_quote( $mask );
668
+
669
+ $mask = str_replace( array(
670
+ '\*',
671
+ '\?',
672
+ ';'
673
+ ), array(
674
+ '@ASTERISK@',
675
+ '@QUESTION@',
676
+ '|'
677
+ ), $mask );
678
+
679
+ $regexp = str_replace( array(
680
+ '@ASTERISK@',
681
+ '@QUESTION@'
682
+ ), array(
683
+ '[^\\?\\*:\\|"<>]*',
684
+ '[^\\?\\*:\\|"<>]'
685
+ ), $mask );
686
+
687
+ return $regexp;
688
+ }
689
+
690
+ /**
691
+ *
692
+ *
693
+ * @param unknown $error
694
+ */
695
+ function update_cnames( &$error ) {
696
+ $common = Dispatcher::component( 'Cdn_Core' );
697
+ $cdn = $common->get_cdn();
698
+ $cdn->update_cnames( $error );
699
+ }
700
+
701
+
702
+ /**
703
+ * media_row_actions filter
704
+ *
705
+ * @param array $actions
706
+ * @param object $post
707
+ * @return array
708
+ */
709
+ function media_row_actions( $actions, $post ) {
710
+ $actions = array_merge( $actions, array(
711
+ 'cdn_purge' => sprintf( '<a href="%s">' . __( 'Purge from CDN', 'w3-total-cache' ) . '</a>', wp_nonce_url( sprintf( 'admin.php?page=w3tc_dashboard&w3tc_cdn_purge_attachment&attachment_id=%d', $post->ID ), 'w3tc' ) )
712
+ ) );
713
+
714
+ return $actions;
715
+ }
716
+
717
+ /**
718
+ * Changes settings on MaxCDN/NetDNA site
719
+ */
720
+ function change_canonical_header() {
721
+ if ( in_array( $cdn_engine = $this->_config->get_string( 'cdn.engine' ), array( 'maxcdn', 'netdna' ) ) ) {
722
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
723
+ $authorization_key = $this->_config->get_string( "cdn.$cdn_engine.authorization_key" );
724
+ if ( $authorization_key ) {
725
+ $keys = explode( '+', $authorization_key );
726
+ if ( sizeof( $keys ) == 3 ) {
727
+ list( $alias, $consumer_key, $consumer_secret ) = $keys;
728
+ $api = new \NetDNA( $alias, $consumer_key, $consumer_secret );
729
+ $zone = array();
730
+ $zone_id = $this->_config->get_string( "cdn.$cdn_engine.zone_id" );
731
+ $zone['canonical_link_headers'] = $this->_config->get_boolean( 'cdn.canonical_header' ) ? 1 : 0;
732
+ try {
733
+ $api->update_pull_zone( $zone_id, $zone );
734
+ } catch ( \Exception $ex ) {}
735
+ }
736
+ }
737
+ }
738
+ }
739
+
740
+ function is_running() {
741
+ /**
742
+ * CDN
743
+ */
744
+ $running = true;
745
+
746
+ /**
747
+ * Check CDN settings
748
+ */
749
+ $cdn_engine = $this->_config->get_string( 'cdn.engine' );
750
+
751
+ switch ( true ) {
752
+ case ( $cdn_engine == 'ftp' && !count( $this->_config->get_array( 'cdn.ftp.domain' ) ) ):
753
+ $running = false;
754
+ break;
755
+
756
+ case ( $cdn_engine == 's3' && ( $this->_config->get_string( 'cdn.s3.key' ) == '' || $this->_config->get_string( 'cdn.s3.secret' ) == '' || $this->_config->get_string( 'cdn.s3.bucket' ) == '' ) ):
757
+ $running = false; break;
758
+
759
+ case ( $cdn_engine == 'cf' && ( $this->_config->get_string( 'cdn.cf.key' ) == '' || $this->_config->get_string( 'cdn.cf.secret' ) == '' || $this->_config->get_string( 'cdn.cf.bucket' ) == '' || ( $this->_config->get_string( 'cdn.cf.id' ) == '' && !count( $this->_config->get_array( 'cdn.cf.cname' ) ) ) ) ):
760
+ $running = false;
761
+ break;
762
+
763
+ case ( $cdn_engine == 'cf2' && ( $this->_config->get_string( 'cdn.cf2.key' ) == '' || $this->_config->get_string( 'cdn.cf2.secret' ) == '' || ( $this->_config->get_string( 'cdn.cf2.id' ) == '' && !count( $this->_config->get_array( 'cdn.cf2.cname' ) ) ) ) ):
764
+ $running = false;
765
+ break;
766
+
767
+ case ( $cdn_engine == 'rscf' && ( $this->_config->get_string( 'cdn.rscf.user' ) == '' || $this->_config->get_string( 'cdn.rscf.key' ) == '' || $this->_config->get_string( 'cdn.rscf.container' ) == '' || !count( $this->_config->get_array( 'cdn.rscf.cname' ) ) ) ):
768
+ $running = false;
769
+ break;
770
+
771
+ case ( $cdn_engine == 'azure' && ( $this->_config->get_string( 'cdn.azure.user' ) == '' || $this->_config->get_string( 'cdn.azure.key' ) == '' || $this->_config->get_string( 'cdn.azure.container' ) == '' ) ):
772
+ $running = false;
773
+ break;
774
+
775
+ case ( $cdn_engine == 'mirror' && !count( $this->_config->get_array( 'cdn.mirror.domain' ) ) ):
776
+ $running = false;
777
+ break;
778
+
779
+ case ( $cdn_engine == 'netdna' ):
780
+ $running = false;
781
+ break;
782
+
783
+ case ( $cdn_engine == 'maxcdn' ):
784
+ $running = false;
785
+ break;
786
+
787
+ case ( $cdn_engine == 'cotendo' && !count( $this->_config->get_array( 'cdn.cotendo.domain' ) ) ):
788
+ $running = false;
789
+ break;
790
+
791
+ case ( $cdn_engine == 'edgecast' && !count( $this->_config->get_array( 'cdn.edgecast.domain' ) ) ):
792
+ $running = false;
793
+ break;
794
+
795
+ case ( $cdn_engine == 'att' && !count( $this->_config->get_array( 'cdn.att.domain' ) ) ):
796
+ $running = false;
797
+ break;
798
+
799
+ case ( $cdn_engine == 'akamai' && !count( $this->_config->get_array( 'cdn.akamai.domain' ) ) ):
800
+ $running = false;
801
+ break;
802
+ }
803
+ return $running;
804
+ }
805
+ }
Cdn_Environment.php ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+
7
+
8
+
9
+ /**
10
+ * class Cdn_Environment
11
+ */
12
+ class Cdn_Environment {
13
+
14
+ /**
15
+ * Fixes environment in each wp-admin request
16
+ *
17
+ * @param Config $config
18
+ * @param bool $force_all_checks
19
+ * @throws Util_Environment_Exceptions
20
+ */
21
+ public function fix_on_wpadmin_request( $config, $force_all_checks ) {
22
+ $exs = new Util_Environment_Exceptions();
23
+
24
+ if ( $config->get_boolean( 'config.check' ) || $force_all_checks ) {
25
+ if ( $config->get_boolean( 'cdn.enabled' ) ) {
26
+ $this->rules_add( $config, $exs );
27
+ } else {
28
+ $this->rules_remove( $exs );
29
+ }
30
+ }
31
+
32
+ if ( count( $exs->exceptions() ) > 0 )
33
+ throw $exs;
34
+ }
35
+
36
+ /**
37
+ * Fixes environment once event occurs
38
+ *
39
+ * @param Config $config
40
+ * @param string $event
41
+ * @param Config|null $old_config
42
+ * @throws Util_Environment_Exceptions
43
+ */
44
+ public function fix_on_event( $config, $event, $old_config = null ) {
45
+ if ( $config->get_boolean( 'cdn.enabled' ) &&
46
+ !Cdn_Util::is_engine_mirror( $config->get_string( 'cdn.engine' ) ) ) {
47
+ if ( $old_config != null &&
48
+ $config->get_integer( 'cdn.queue.interval' ) !=
49
+ $old_config->get_integer( 'cdn.queue.interval' ) ) {
50
+ $this->unschedule_queue_process();
51
+ }
52
+
53
+ if ( !wp_next_scheduled( 'w3_cdn_cron_queue_process' ) ) {
54
+ wp_schedule_event( time(),
55
+ 'w3_cdn_cron_queue_process', 'w3_cdn_cron_queue_process' );
56
+ }
57
+ } else {
58
+ $this->unschedule_queue_process();
59
+ }
60
+
61
+ if ( $config->get_boolean( 'cdn.enabled' ) &&
62
+ $config->get_boolean( 'cdn.autoupload.enabled' ) &&
63
+ !Cdn_Util::is_engine_mirror( $config->get_string( 'cdn.engine' ) ) ) {
64
+ if ( $old_config != null &&
65
+ $config->get_integer( 'cdn.autoupload.interval' ) !=
66
+ $old_config->get_integer( 'cdn.autoupload.interval' ) ) {
67
+ $this->unschedule_upload();
68
+ }
69
+
70
+ if ( !wp_next_scheduled( 'w3_cdn_cron_upload' ) ) {
71
+ wp_schedule_event( time(),
72
+ 'w3_cdn_cron_upload', 'w3_cdn_cron_upload' );
73
+ }
74
+ } else {
75
+ $this->unschedule_upload();
76
+ }
77
+
78
+ $exs = new Util_Environment_Exceptions();
79
+
80
+ if ( $config->get_boolean( 'cdn.enabled' ) ) {
81
+ try {
82
+ $this->table_create( $event == 'activate' );
83
+ } catch ( \Exception $ex ) {
84
+ $exs->push( $ex );
85
+ }
86
+ }
87
+
88
+ if ( count( $exs->exceptions() ) > 0 )
89
+ throw $exs;
90
+ }
91
+
92
+ /**
93
+ * Fixes environment after plugin deactivation
94
+ */
95
+ public function fix_after_deactivation() {
96
+ $exs = new Util_Environment_Exceptions();
97
+
98
+ $this->rules_remove( $exs );
99
+ $this->table_delete();
100
+
101
+ if ( count( $exs->exceptions() ) > 0 )
102
+ throw $exs;
103
+ }
104
+
105
+ /**
106
+ * Returns required rules for module
107
+ *
108
+ * @param Config $config
109
+ * @return array|null
110
+ */
111
+ function get_required_rules( $config ) {
112
+ if ( !$config->get_boolean( 'cdn.enabled' ) )
113
+ return null;
114
+
115
+ $rewrite_rules = array();
116
+ $rules = $this->rules_generate( $config );
117
+
118
+ if ( strlen( $rules ) > 0 ) {
119
+ if ( $config->get_string( 'cdn.engine' ) == 'ftp' ) {
120
+ $common = Dispatcher::component( 'Cdn_Core' );
121
+ $domain = $common->get_cdn()->get_domain();
122
+ $cdn_rules_path = sprintf( 'ftp://%s/%s', $domain,
123
+ Util_Rule::get_cdn_rules_path() );
124
+ $rewrite_rules[] = array(
125
+ 'filename' => $cdn_rules_path,
126
+ 'content' => $rules
127
+ );
128
+ }
129
+
130
+ $path = Util_Rule::get_browsercache_rules_cache_path();
131
+ $rewrite_rules[] = array(
132
+ 'filename' => $path,
133
+ 'content' => $rules
134
+ );
135
+ }
136
+ return $rewrite_rules;
137
+ }
138
+
139
+ /**
140
+ *
141
+ *
142
+ * @param Config $config
143
+ * @return array|null
144
+ */
145
+ function get_instructions( $config ) {
146
+ if ( !$config->get_boolean( 'cdn.enabled' ) )
147
+ return null;
148
+
149
+ $instructions = array();
150
+ $instructions[] = array( 'title'=>__( 'CDN module: Required Database SQL', 'w3-total-cache' ),
151
+ 'content' => $this->generate_table_sql(), 'area' => 'database' );
152
+
153
+ return $instructions;
154
+ }
155
+
156
+ /**
157
+ * Generate rules for FTP
158
+ */
159
+ public function rules_generate_for_ftp( $config ) {
160
+ return $this->rules_generate( $config, true );
161
+ }
162
+
163
+
164
+
165
+ /*
166
+ * table operations
167
+ */
168
+
169
+ /**
170
+ * Create queue table
171
+ *
172
+ * @param bool $drop
173
+ * @throws Util_Environment_Exception
174
+ */
175
+ private function table_create( $drop = false ) {
176
+ global $wpdb;
177
+
178
+ if ( $drop ) {
179
+ $sql = sprintf( 'DROP TABLE IF EXISTS `%s%s`;', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
180
+
181
+ $wpdb->query( $sql );
182
+ }
183
+
184
+ $charset_collate = '';
185
+
186
+ if ( ! empty( $wpdb->charset ) )
187
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
188
+ if ( ! empty( $wpdb->collate ) )
189
+ $charset_collate .= " COLLATE $wpdb->collate";
190
+
191
+ $sql = sprintf( "CREATE TABLE IF NOT EXISTS `%s%s` (
192
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
193
+ `local_path` varchar(500) NOT NULL DEFAULT '',
194
+ `remote_path` varchar(500) NOT NULL DEFAULT '',
195
+ `command` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 - Upload, 2 - Delete, 3 - Purge',
196
+ `last_error` varchar(150) NOT NULL DEFAULT '',
197
+ `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
198
+ PRIMARY KEY (`id`),
199
+ KEY `date` (`date`)
200
+ ) $charset_collate;", $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
201
+
202
+ $wpdb->query( $sql );
203
+
204
+ if ( !$wpdb->result )
205
+ throw new Util_Environment_Exception( 'Can\'t create table ' .
206
+ $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE );
207
+ }
208
+
209
+ /**
210
+ * Delete queue table
211
+ *
212
+ * @return void
213
+ */
214
+ private function table_delete() {
215
+ global $wpdb;
216
+
217
+ $sql = sprintf( 'DROP TABLE IF EXISTS `%s%s`', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
218
+ $wpdb->query( $sql );
219
+ }
220
+
221
+ private function generate_table_sql() {
222
+ global $wpdb;
223
+ $charset_collate = '';
224
+
225
+ if ( ! empty( $wpdb->charset ) )
226
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
227
+ if ( ! empty( $wpdb->collate ) )
228
+ $charset_collate .= " COLLATE $wpdb->collate";
229
+
230
+ $sql = sprintf( 'DROP TABLE IF EXISTS `%s%s`;', $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
231
+ $sql .= "\n" . sprintf( "CREATE TABLE IF NOT EXISTS `%s%s` (
232
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
233
+ `local_path` varchar(500) NOT NULL DEFAULT '',
234
+ `remote_path` varchar(500) NOT NULL DEFAULT '',
235
+ `command` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 - Upload, 2 - Delete, 3 - Purge',
236
+ `last_error` varchar(150) NOT NULL DEFAULT '',
237
+ `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
238
+ PRIMARY KEY (`id`),
239
+ KEY `date` (`date`)
240
+ ) $charset_collate;", $wpdb->base_prefix, W3TC_CDN_TABLE_QUEUE );
241
+
242
+ return $sql;
243
+ }
244
+
245
+ /**
246
+ * schedules
247
+ */
248
+
249
+ /**
250
+ * Unschedules cron events
251
+ */
252
+ private function unschedule_queue_process() {
253
+ if ( wp_next_scheduled( 'w3_cdn_cron_queue_process' ) ) {
254
+ wp_clear_scheduled_hook( 'w3_cdn_cron_queue_process' );
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Unschedule upload event
260
+ */
261
+ private function unschedule_upload() {
262
+ if ( wp_next_scheduled( 'w3_cdn_cron_upload' ) ) {
263
+ wp_clear_scheduled_hook( 'w3_cdn_cron_upload' );
264
+ }
265
+ }
266
+
267
+
268
+
269
+ /*
270
+ * rules core modification
271
+ */
272
+
273
+ /**
274
+ * Writes directives to WP .htaccess
275
+ *
276
+ * @param Config $config
277
+ * @param Util_Environment_Exceptions $exs
278
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
279
+ */
280
+ private function rules_add( $config, $exs ) {
281
+ Util_Rule::add_rules( $exs, Util_Rule::get_browsercache_rules_cache_path(),
282
+ $this->rules_generate( $config ),
283
+ W3TC_MARKER_BEGIN_CDN,
284
+ W3TC_MARKER_END_CDN,
285
+ array(
286
+ W3TC_MARKER_BEGIN_MINIFY_CORE => 0,
287
+ W3TC_MARKER_BEGIN_PGCACHE_CORE => 0,
288
+ W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP => 0,
289
+ W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE => 0,
290
+ W3TC_MARKER_BEGIN_WORDPRESS => 0,
291
+ W3TC_MARKER_END_PGCACHE_CACHE => strlen( W3TC_MARKER_END_PGCACHE_CACHE ) + 1,
292
+ W3TC_MARKER_END_MINIFY_CACHE => strlen( W3TC_MARKER_END_MINIFY_CACHE ) + 1
293
+ )
294
+ );
295
+ }
296
+
297
+ /**
298
+ * Removes Page Cache core directives
299
+ *
300
+ * @param Util_Environment_Exceptions $exs
301
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
302
+ */
303
+ private function rules_remove( $exs ) {
304
+ Util_Rule::remove_rules( $exs,
305
+ Util_Rule::get_browsercache_rules_cache_path(),
306
+ W3TC_MARKER_BEGIN_CDN,
307
+ W3TC_MARKER_END_CDN );
308
+ }
309
+
310
+ /**
311
+ * Generates rules for WP dir
312
+ *
313
+ * @param Config $config
314
+ * @param bool $cdnftp
315
+ * @return string
316
+ */
317
+ private function rules_generate( $config, $cdnftp = false ) {
318
+ $rules = '';
319
+ if ( Dispatcher::canonical_generated_by( $config, $cdnftp ) == 'cdn' )
320
+ $rules .= Util_RuleSnippet::canonical( $config, $cdnftp );
321
+ if ( Dispatcher::allow_origin_generated_by( $config ) == 'cdn' )
322
+ $rules .= Util_RuleSnippet::allow_origin( $config, $cdnftp );
323
+
324
+ if ( strlen( $rules ) > 0 )
325
+ $rules =
326
+ W3TC_MARKER_BEGIN_CDN . "\n" .
327
+ $rules .
328
+ W3TC_MARKER_END_CDN . "\n";
329
+
330
+ return $rules;
331
+ }
332
+ }
Cdn_Fsd_CacheFlush.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * CDN cache flusher
6
+ */
7
+ class Cdn_Fsd_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 = array() ) {
22
+ if ( isset( $extras['only'] ) && $extras['only'] != 'cdn' )
23
+ return;
24
+
25
+ $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' );
26
+
27
+ $o->flush_all_requested = true;
28
+ return true;
29
+ }
30
+
31
+
32
+
33
+ /**
34
+ * Purges cdn's post cache
35
+ *
36
+ * @param integer $post_id
37
+ * @return boolean
38
+ */
39
+ static public function w3tc_flush_post( $post_id ) {
40
+ if ( !$post_id ) {
41
+ $post_id = Util_Environment::detect_post_id();
42
+ }
43
+
44
+ if ( !$post_id )
45
+ return false;
46
+
47
+ $config = Dispatcher::config();
48
+ $full_urls = array();
49
+ $post = null;
50
+ $terms = array();
51
+
52
+ $feeds = $config->get_array( 'pgcache.purge.feed.types' );
53
+ $limit_post_pages = $config->get_integer( 'pgcache.purge.postpages_limit' );
54
+
55
+ if ( $config->get_boolean( 'pgcache.purge.terms' ) || $config->get_boolean( 'varnish.pgcache.feed.terms' ) ) {
56
+ $taxonomies = get_post_taxonomies( $post_id );
57
+ $terms = wp_get_post_terms( $post_id, $taxonomies );
58
+ }
59
+
60
+ switch ( true ) {
61
+ case $config->get_boolean( 'pgcache.purge.author' ):
62
+ case $config->get_boolean( 'pgcache.purge.archive.daily' ):
63
+ case $config->get_boolean( 'pgcache.purge.archive.monthly' ):
64
+ case $config->get_boolean( 'pgcache.purge.archive.yearly' ):
65
+ case $config->get_boolean( 'pgcache.purge.feed.author' ):
66
+ $post = get_post( $post_id );
67
+ }
68
+
69
+ $front_page = get_option( 'show_on_front' );
70
+
71
+ /**
72
+ * Home (Frontpage) URL
73
+ */
74
+ if ( ( $config->get_boolean( 'pgcache.purge.home' ) && $front_page == 'posts' )||
75
+ $config->get_boolean( 'pgcache.purge.front_page' ) ) {
76
+ $full_urls = array_merge( $full_urls,
77
+ Util_PageUrls::get_frontpage_urls( $limit_post_pages ) );
78
+ }
79
+
80
+ /**
81
+ * Home (Post page) URL
82
+ */
83
+ if ( $config->get_boolean( 'pgcache.purge.home' ) && $front_page != 'posts' ) {
84
+ $full_urls = array_merge( $full_urls,
85
+ Util_PageUrls::get_postpage_urls( $limit_post_pages ) );
86
+ }
87
+
88
+ /**
89
+ * Post URL
90
+ */
91
+ if ( $config->get_boolean( 'pgcache.purge.post' ) ) {
92
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_urls( $post_id ) );
93
+ }
94
+
95
+ /**
96
+ * Post comments URLs
97
+ */
98
+ if ( $config->get_boolean( 'pgcache.purge.comments' ) && function_exists( 'get_comments_pagenum_link' ) ) {
99
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_comments_urls( $post_id ) );
100
+ }
101
+
102
+ /**
103
+ * Post author URLs
104
+ */
105
+ if ( $config->get_boolean( 'pgcache.purge.author' ) && $post ) {
106
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_author_urls( $post->post_author, $limit_post_pages ) );
107
+ }
108
+
109
+ /**
110
+ * Post terms URLs
111
+ */
112
+ if ( $config->get_boolean( 'pgcache.purge.terms' ) ) {
113
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_post_terms_urls( $terms, $limit_post_pages ) );
114
+ }
115
+
116
+ /**
117
+ * Daily archive URLs
118
+ */
119
+ if ( $config->get_boolean( 'pgcache.purge.archive.daily' ) && $post ) {
120
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_daily_archive_urls( $post, $limit_post_pages ) );
121
+ }
122
+
123
+ /**
124
+ * Monthly archive URLs
125
+ */
126
+ if ( $config->get_boolean( 'pgcache.purge.archive.monthly' ) && $post ) {
127
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_monthly_archive_urls( $post, $limit_post_pages ) );
128
+ }
129
+
130
+ /**
131
+ * Yearly archive URLs
132
+ */
133
+ if ( $config->get_boolean( 'pgcache.purge.archive.yearly' ) && $post ) {
134
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_yearly_archive_urls( $post, $limit_post_pages ) );
135
+ }
136
+
137
+ /**
138
+ * Feed URLs
139
+ */
140
+ if ( $config->get_boolean( 'pgcache.purge.feed.blog' ) ) {
141
+ $full_urls = array_merge( $full_urls,
142
+ Util_PageUrls::get_feed_urls( $feeds ) );
143
+ }
144
+
145
+ if ( $config->get_boolean( 'pgcache.purge.feed.comments' ) ) {
146
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_comments_urls( $post_id, $feeds ) );
147
+ }
148
+
149
+ if ( $config->get_boolean( 'pgcache.purge.feed.author' ) && $post ) {
150
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_author_urls( $post->post_author, $feeds ) );
151
+ }
152
+
153
+ if ( $config->get_boolean( 'pgcache.purge.feed.terms' ) ) {
154
+ $full_urls = array_merge( $full_urls, Util_PageUrls::get_feed_terms_urls( $terms, $feeds ) );
155
+ }
156
+
157
+ /**
158
+ * Purge selected pages
159
+ */
160
+ if ( $config->get_array( 'pgcache.purge.pages' ) ) {
161
+ $pages = $config->get_array( 'pgcache.purge.pages' );
162
+ $full_urls = array_merge( $full_urls,
163
+ Util_PageUrls::get_pages_urls( $pages ) );
164
+ }
165
+
166
+ /**
167
+ * Queue flush
168
+ */
169
+ if ( count( $full_urls ) ) {
170
+ $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' );
171
+
172
+ foreach ( $full_urls as $url )
173
+ $o->queued_urls[$url] = '*';
174
+ }
175
+
176
+ return true;
177
+ }
178
+
179
+ /**
180
+ * Purge a single url
181
+ *
182
+ * @param unknown $url
183
+ */
184
+ static public function w3tc_flush_url( $url ) {
185
+ $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' );
186
+ $o->queued_urls[$url] = '*';
187
+
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * Clears global and repeated urls
193
+ */
194
+ static public function w3tc_flush_execute_delayed_operations( $actions_made ) {
195
+ $o = Dispatcher::component( 'Cdn_Fsd_CacheFlush' );
196
+
197
+ if ( $o->flush_all_requested ) {
198
+ $core = Dispatcher::component( 'Cdn_Fsd_Core' );
199
+ $engine = $core->get_engine();
200
+ try {
201
+ $engine->flush_all();
202
+ $actions_made[] = array( 'module' => 'cdn' );
203
+ } catch ( \Exception $ex ) {
204
+ $actions_made[] = array(
205
+ 'module' => 'cdn',
206
+ 'error' => $ex->getMessage()
207
+ );
208
+ }
209
+
210
+ $o->flush_all_requested = false;
211
+ $o->queued_urls = array();
212
+ } else {
213
+ $count = count( $o->queued_urls );
214
+ if ( $count > 0 ) {
215
+ $urls = array_keys( $o->queued_urls );
216
+
217
+ $core = Dispatcher::component( 'Cdn_Fsd_Core' );
218
+ $engine = $core->get_engine();
219
+ try {
220
+ $engine->flush_urls( $urls );
221
+ $actions_made[] = array( 'module' => 'cdn' );
222
+ } catch ( \Exception $ex ) {
223
+ $actions_made[] = array(
224
+ 'module' => 'cdn',
225
+ 'error' => $ex->getMessage()
226
+ );
227
+ }
228
+
229
+ $o->queued_urls = array();
230
+ }
231
+ }
232
+
233
+ return $actions_made;
234
+ }
235
+ }
Cdn_Fsd_Core.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Core for FSD CDN
6
+ */
7
+ class Cdn_Fsd_Core {
8
+ /**
9
+ * Returns CDN object
10
+ */
11
+ function get_engine() {
12
+ static $engine_object = null;
13
+
14
+ if ( is_null( $engine_object ) ) {
15
+ $c = Dispatcher::config();
16
+ $engine = $c->get_string( 'cdn.engine' );
17
+
18
+ switch ( $engine ) {
19
+ case 'cloudfront_fsd':
20
+ $engine_object = new Cdn_CloudFrontFsd_Engine( array(
21
+ 'access_key' => $c->get_string( 'cdn.cloudfront_fsd.access_key' ),
22
+ 'secret_key' => $c->get_string( 'cdn.cloudfront_fsd.secret_key' ),
23
+ 'distribution_id' => $c->get_string( 'cdn.cloudfront_fsd.distribution_id' )
24
+ ) );
25
+ break;
26
+
27
+ case 'maxcdn_fsd':
28
+ $engine_object = new Cdn_MaxCdnFsd_Engine( array(
29
+ 'api_key' => $c->get_string( 'cdn.maxcdn_fsd.api_key' ),
30
+ 'zone_id' => $c->get_integer( 'cdn.maxcdn_fsd.zone_id' )
31
+ ) );
32
+ break;
33
+
34
+ default:
35
+ throw new \Exception( 'unknown engine ' . $engine );
36
+ }
37
+ }
38
+
39
+ return $engine_object;
40
+ }
41
+ }
Cdn_Fsd_Util.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_Fsd_Util {
5
+ static public function get_suggested_home_ip() {
6
+ $ip = gethostbyname( Util_Environment::home_url_host() );
7
+
8
+ // check if it resolves to local IP, means host cant know its real IP
9
+ if ( substr( $ip, 0, 4 ) == '127.' ||
10
+ substr( $ip, 0, 3 ) == '10.' ||
11
+ substr( $ip, 0, 8 ) == '192.168.' )
12
+ return '';
13
+
14
+ return $ip;
15
+ }
16
+ }
Cdn_GeneralPage_View.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ Util_Ui::postbox_header( __( '<acronym title="Content Delivery Network">CDN</acronym>', 'w3-total-cache' ), '', 'cdn' );
8
+ Util_Ui::config_overloading_button( array(
9
+ 'key' => 'cdn.configuration_overloaded'
10
+ ) );
11
+ ?>
12
+ <p><?php _e( 'Host static files with your content delivery network provider to reduce page load time.', 'w3-total-cache' ); ?>
13
+ <?php if ( !$cdn_enabled ): ?>
14
+ <?php printf( __( 'If you do not have a <acronym title="Content Delivery Network">CDN</acronym> provider try MaxCDN. <a href="%s" target="_blank">Sign up and save 25&#37;</a>.', 'w3-total-cache' ), wp_nonce_url( Util_Ui::admin_url( 'admin.php?page=w3tc_dashboard&w3tc_cdn_maxcdn_signup' ), 'w3tc' ) ); ?>
15
+ <?php endif ?>
16
+ </p>
17
+ <table class="form-table">
18
+ <?php
19
+ Util_Ui::config_item( array(
20
+ 'key' => 'cdn.enabled',
21
+ 'control' => 'checkbox',
22
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
23
+ 'description' => __( 'Theme files, media library attachments, <acronym title="Cascading Style Sheet">CSS</acronym>, <acronym title="JavaScript">JS</acronym> files etc will appear to load instantly for site visitors.',
24
+ 'w3-total-cache' )
25
+ ) );
26
+
27
+ Util_Ui::config_item( array(
28
+ 'key' => 'cdn.engine',
29
+ 'control' => 'selectbox',
30
+ 'selectbox_values' => $engine_values,
31
+ 'selectbox_optgroups' => $engine_optgroups,
32
+ 'description' => __( 'Select the <acronym title="Content Delivery Network">CDN</acronym> type you wish to use.',
33
+ 'w3-total-cache' ) . $cdn_engine_extra_description
34
+ ) );
35
+ ?>
36
+ </table>
37
+
38
+ <?php
39
+ Util_Ui::button_config_save( 'general_cdn',
40
+ '<input id="cdn_purge" type="button" value="'.
41
+ __( 'Empty cache', 'w3-total-cache' ) . '" ' .
42
+ ( $cdn_enabled && Cdn_Util::can_purge_all( $config->get_string( 'cdn.engine' ) ) ? '' :
43
+ ' disabled="disabled" ' ) .
44
+ ' class="button {nonce: \'' . wp_create_nonce( 'w3tc' ) . '\'}" />' );
45
+ ?>
46
+ <?php Util_Ui::postbox_footer(); ?>
Cdn_GoogleDrive_AdminActions.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_GoogleDrive_AdminActions {
7
+ private $_config = null;
8
+
9
+ function __construct() {
10
+ $this->_config = Dispatcher::config();
11
+ }
12
+
13
+
14
+
15
+ function w3tc_cdn_google_drive_auth_return() {
16
+ $view = new Cdn_GoogleDrive_Popup_AuthReturn();
17
+ $view->render();
18
+ exit();
19
+ }
20
+
21
+
22
+
23
+ function w3tc_cdn_google_drive_auth_set() {
24
+ // thanks wp core for wp_magic_quotes hell
25
+ $client_id = stripslashes( $_POST['client_id'] );
26
+ $access_token = stripslashes( $_POST['access_token'] );
27
+ $refresh_token = stripslashes( $_POST['refresh_token'] );
28
+
29
+ $client = new \Google_Client();
30
+ $client->setClientId( $client_id );
31
+ $client->setAccessToken( $access_token );
32
+
33
+ //
34
+ // get folder details
35
+ //
36
+ $service = new \Google_Service_Drive( $client );
37
+
38
+ if ( empty( $_POST['folder'] ) ) {
39
+ $file = new \Google_Service_Drive_DriveFile( array(
40
+ 'title' => $_POST['folder_new'],
41
+ 'mimeType' => 'application/vnd.google-apps.folder' ) );
42
+
43
+ $created_file = $service->files->insert( $file );
44
+ $used_folder_id = $created_file->id;
45
+ } else {
46
+ $used_folder_id = $_POST['folder'];
47
+ }
48
+
49
+ $permission = new \Google_Service_Drive_Permission();
50
+ $permission->setValue( '' );
51
+ $permission->setType( 'anyone' );
52
+ $permission->setRole( 'reader' );
53
+
54
+ $service->permissions->insert( $used_folder_id, $permission );
55
+
56
+ $used_folder = $service->files->get( $used_folder_id );
57
+
58
+
59
+ //
60
+ // save new configuration
61
+ //
62
+ delete_transient( 'w3tc_cdn_google_drive_folder_ids' );
63
+ $this->_config->set( 'cdn.google_drive.client_id', $client_id );
64
+ $this->_config->set( 'cdn.google_drive.refresh_token', $refresh_token );
65
+ $this->_config->set( 'cdn.google_drive.folder.id', $used_folder->id );
66
+ $this->_config->set( 'cdn.google_drive.folder.title',
67
+ $used_folder->title );
68
+ $this->_config->set( 'cdn.google_drive.folder.url',
69
+ $used_folder->webViewLink );
70
+ $this->_config->save();
71
+
72
+
73
+ $cs = Dispatcher::config_state();
74
+ $cs->set( 'cdn.google_drive.access_token', $access_token );
75
+ $cs->save();
76
+
77
+ wp_redirect( 'admin.php?page=w3tc_cdn', false );
78
+ }
79
+ }
Cdn_GoogleDrive_Page.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_GoogleDrive_Page {
5
+ // called from plugin-admin
6
+ static public function admin_print_scripts_w3tc_cdn() {
7
+ wp_enqueue_script( 'w3tc_cdn_google_drive',
8
+ plugins_url( 'Cdn_GoogleDrive_Page_View.js', W3TC_FILE ),
9
+ array( 'jquery' ), '1.0' );
10
+
11
+ $path = 'admin.php?page=w3tc_cdn';
12
+ $return_url = self_admin_url( $path );
13
+
14
+ wp_localize_script( 'w3tc_cdn_google_drive',
15
+ 'w3tc_cdn_google_drive_url',
16
+ GOOGLE_DRIVE_AUTHORIZE_URL . '?return_url=' . urlencode( $return_url ) );
17
+
18
+ // it's return from google oauth
19
+ if ( isset( $_GET['oa_client_id'] ) ) {
20
+ $path = wp_nonce_url( 'admin.php', 'w3tc' ) .
21
+ '&page=w3tc_cdn&w3tc_cdn_google_drive_auth_return';
22
+ foreach ( $_GET as $key => $value ) {
23
+ if ( substr( $key, 0, 3 ) == 'oa_' )
24
+ $path .= '&' . urlencode( $key ) . '=' . urlencode( $value );
25
+ }
26
+
27
+ $popup_url = self_admin_url( $path );
28
+
29
+ wp_localize_script( 'w3tc_cdn_google_drive',
30
+ 'w3tc_cdn_google_drive_popup_url', $popup_url );
31
+
32
+ }
33
+ }
34
+
35
+
36
+
37
+ static public function w3tc_settings_cdn_boxarea_configuration() {
38
+ $config = Dispatcher::config();
39
+ include W3TC_DIR . '/Cdn_GoogleDrive_Page_View.php';
40
+ }
41
+ }
Cdn_GoogleDrive_Page_View.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ $('.w3tc_cdn_google_drive_authorize').click(function() {
3
+ window.location = w3tc_cdn_google_drive_url;
4
+ });
5
+
6
+ if (window.w3tc_cdn_google_drive_popup_url) {
7
+ W3tc_Lightbox.open({
8
+ id:'w3tc-overlay',
9
+ close: '',
10
+ width: 800,
11
+ height: 500,
12
+ url: w3tc_cdn_google_drive_popup_url,
13
+ onClose: function() {
14
+ }
15
+ });
16
+ }
17
+ });
Cdn_GoogleDrive_Page_View.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ $refresh_token = $config->get_string( 'cdn.google_drive.refresh_token' );
8
+
9
+ ?>
10
+ <tr>
11
+ <th style="width: 300px;"><label><?php _e( 'Authorize:', 'w3-total-cache' ); ?></label></th>
12
+ <td>
13
+ <?php if ( empty( $refresh_token ) ): ?>
14
+ <input class="w3tc_cdn_google_drive_authorize button" type="button"
15
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>" />
16
+ <?php else: ?>
17
+ <input class="w3tc_cdn_google_drive_authorize button" type="button"
18
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>" />
19
+ <?php endif ?>
20
+ </td>
21
+ </tr>
22
+
23
+ <?php if ( !empty( $refresh_token ) ): ?>
24
+ <tr>
25
+ <th><label for="cdn_s3_bucket"><?php _e( 'Folder:', 'w3-total-cache' ); ?></label></th>
26
+ <td>
27
+ <a href="<?php echo $config->get_string( 'cdn.google_drive.folder.url' ) ?>">/<?php echo $config->get_string( 'cdn.google_drive.folder.title' ) ?></a>
28
+ </td>
29
+ </tr>
30
+ <tr>
31
+ <th colspan="2">
32
+ <input id="cdn_test"
33
+ class="button {type: 'google_drive', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}"
34
+ type="button"
35
+ value="<?php _e( 'Test upload', 'w3-total-cache' ); ?>" />
36
+ <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
37
+ </th>
38
+ </tr>
39
+ <?php endif ?>
Cdn_GoogleDrive_Popup_AuthReturn.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_GoogleDrive_Popup_AuthReturn {
7
+ function render() {
8
+ $client_id = $_GET['oa_client_id'];
9
+ $refresh_token = $_GET['oa_refresh_token'];
10
+
11
+ $token_array = array(
12
+ 'access_token' => $_GET['oa_access_token'],
13
+ 'token_type' => $_GET['oa_token_type'],
14
+ 'expires_in' => $_GET['oa_expires_in'],
15
+ 'created' => $_GET['oa_created']
16
+ );
17
+ $access_token = json_encode( $token_array );
18
+
19
+ $client = new \Google_Client();
20
+ $client->setClientId( $client_id );
21
+ $client->setAccessToken( $access_token );
22
+
23
+
24
+ $service = new \Google_Service_Drive( $client );
25
+
26
+ $items = $service->files->listFiles( array(
27
+ 'q' => "mimeType = 'application/vnd.google-apps.folder'"
28
+ ) );
29
+
30
+ $folders = array();
31
+ foreach ( $items as $item ) {
32
+ if ( count( $item->parents ) > 0 && $item->parents[0]->isRoot )
33
+ $folders[] = $item;
34
+ }
35
+
36
+ include W3TC_DIR . '/Cdn_GoogleDrive_Popup_AuthReturn_View.php';
37
+ }
38
+ }
Cdn_GoogleDrive_Popup_AuthReturn_View.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px">
8
+ <?php
9
+ Util_Ui::hidden( '', 'client_id', $client_id );
10
+ Util_Ui::hidden( '', 'access_token', $access_token );
11
+ Util_Ui::hidden( '', 'refresh_token', $refresh_token );
12
+ echo Util_Ui::nonce_field( 'w3tc' );
13
+ ?>
14
+ <br /><br />
15
+ <div class="metabox-holder">
16
+ <?php Util_Ui::postbox_header( __( 'Select folder', 'w3-total-cache' ) ); ?>
17
+ <table class="form-table">
18
+ <tr>
19
+ <td>Folder:</td>
20
+ <td>
21
+ <?php foreach ( $folders as $folder ): ?>
22
+ <label>
23
+ <input name="folder" type="radio" class="w3tc-ignore-change"
24
+ value="<?php echo $folder->id ?>" />
25
+ <?php echo $folder->title ?>
26
+ </label><br />
27
+ <?php endforeach ?>
28
+
29
+ <label>
30
+ <input name="folder" type="radio" class="w3tc-ignore-change" value=""
31
+ />
32
+ Add new folder:
33
+ </label>
34
+ <input name="folder_new" type="text" class="w3tc-ignore-change" />
35
+ </tr>
36
+ </table>
37
+
38
+ <p class="submit">
39
+ <input type="submit" name="w3tc_cdn_google_drive_auth_set"
40
+ class="w3tc-button-save button-primary"
41
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
42
+ </p>
43
+ <?php Util_Ui::postbox_footer(); ?>
44
+ </div>
45
+ </form>
Cdn_Highwinds_Api.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_Highwinds_Api {
7
+ static private $root_uri = 'https://striketracker3.highwinds.com';
8
+ private $account_hash;
9
+ private $api_token;
10
+
11
+
12
+
13
+ static public function users_me( $api_token ) {
14
+ $result = wp_remote_get( self::$root_uri . '/api/v1/users/me', array(
15
+ 'headers' => 'Authorization: Bearer ' . $api_token
16
+ ) );
17
+
18
+ $r = self::_decode_response( $result );
19
+ if ( !$r['auth_required'] )
20
+ return $r['response_json'];
21
+
22
+ throw new \Exception( 'Authentication failed' );
23
+ }
24
+
25
+
26
+
27
+ public function __construct( $account_hash, $api_token ) {
28
+ $this->account_hash = $account_hash;
29
+ $this->api_token = $api_token;
30
+ }
31
+
32
+
33
+
34
+ public function analytics_transfer( $host, $granularity, $platforms,
35
+ $start_date, $end_date ) {
36
+ return $this->_wp_remote_get(
37
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash .
38
+ '/analytics/transfer?startDate=' . urlencode( $start_date ) .
39
+ '&endDate=' . urlencode( $end_date ) .
40
+ '&granularity=' . urlencode( $granularity ) .
41
+ '&platforms=' . urlencode( $platforms ) );
42
+ }
43
+
44
+
45
+
46
+ public function configure_scopes( $host ) {
47
+ return $this->_wp_remote_get(
48
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash .
49
+ '/hosts/' . $host . '/configuration/scopes' );
50
+ }
51
+
52
+
53
+
54
+ public function configure_scope_get( $host, $scope_id ) {
55
+ return $this->_wp_remote_get(
56
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash .
57
+ '/hosts/' . $host . '/configuration/' . $scope_id );
58
+ }
59
+
60
+
61
+
62
+ public function configure_scope_set( $host, $scope_id, $configuration ) {
63
+ return $this->_wp_remote_put(
64
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash .
65
+ '/hosts/' . $host . '/configuration/' . $scope_id,
66
+ json_encode( $configuration )
67
+ );
68
+ }
69
+
70
+
71
+
72
+ public function host_add( $host ) {
73
+ return $this->_wp_remote_post(
74
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/hosts',
75
+ json_encode( $host )
76
+ );
77
+ }
78
+
79
+
80
+
81
+ public function hosts() {
82
+ return $this->_wp_remote_get(
83
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/hosts' );
84
+ }
85
+
86
+
87
+
88
+ public function origin_add( $origin ) {
89
+ return $this->_wp_remote_post(
90
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/origins',
91
+ json_encode( $origin )
92
+ );
93
+ }
94
+
95
+
96
+
97
+ public function origins() {
98
+ return $this->_wp_remote_get(
99
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/origins' );
100
+ }
101
+
102
+
103
+
104
+ /**
105
+ * $recursive - true/false
106
+ */
107
+ public function purge( $urls, $recursive ) {
108
+ $list = array();
109
+ foreach ( $urls as $url ) {
110
+ $list[] = array(
111
+ 'url' => $url,
112
+ 'recursive' => $recursive );
113
+ }
114
+
115
+ $response = $this->_wp_remote_post(
116
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/purge',
117
+ json_encode( array( 'list' => $list ) )
118
+ );
119
+ }
120
+
121
+
122
+
123
+ public function services() {
124
+ return $this->_wp_remote_get(
125
+ self::$root_uri . '/api/v1/accounts/' . $this->account_hash . '/services'
126
+ );
127
+ }
128
+
129
+
130
+
131
+ private function _wp_remote_get( $url, $body = array() ) {
132
+ $result = wp_remote_get( $url, array(
133
+ 'headers' => 'Authorization: Bearer ' . $this->api_token,
134
+ 'body' => $body
135
+ ) );
136
+
137
+ $r = self::_decode_response( $result );
138
+ if ( !$r['auth_required'] )
139
+ return $r['response_json'];
140
+
141
+ throw new \Exception( 'Authentication failed' );
142
+ }
143
+
144
+
145
+
146
+ private function _wp_remote_post( $url, $body ) {
147
+ $headers = array(
148
+ 'Authorization' => 'Bearer ' . $this->api_token
149
+ );
150
+ if ( !is_array( $body ) )
151
+ $headers['Content-Type'] = 'application/json';
152
+
153
+ $result = wp_remote_post( $url, array(
154
+ 'headers' => $headers,
155
+ 'body' => $body
156
+ ) );
157
+
158
+ $r = self::_decode_response( $result );
159
+ if ( !$r['auth_required'] )
160
+ return $r['response_json'];
161
+
162
+ throw new \Exception( 'Authentication failed' );
163
+ }
164
+
165
+
166
+
167
+ private function _wp_remote_put( $url, $body ) {
168
+ $headers = array(
169
+ 'Authorization' => 'Bearer ' . $this->api_token
170
+ );
171
+ if ( !is_array( $body ) )
172
+ $headers['Content-Type'] = 'application/json';
173
+
174
+ $result = wp_remote_post( $url, array(
175
+ 'headers' => $headers,
176
+ 'body' => $body,
177
+ 'method' => 'PUT'
178
+ ) );
179
+
180
+ $r = self::_decode_response( $result );
181
+ if ( !$r['auth_required'] )
182
+ return $r['response_json'];
183
+
184
+ throw new \Exception( 'Authentication failed' );
185
+ }
186
+
187
+
188
+
189
+ static private function _decode_response( $result ) {
190
+ if ( is_wp_error( $result ) )
191
+ throw new \Exception( 'Failed to reach API endpoint' );
192
+
193
+ $response_json = @json_decode( $result['body'], true );
194
+ if ( is_null( $response_json ) ) {
195
+ if ( $result['response']['code'] == '200' && empty( $result['body'] ) )
196
+ return array(
197
+ 'response_json' => array(),
198
+ 'auth_required' => false
199
+ );
200
+
201
+ throw new \Exception(
202
+ 'Failed to reach API endpoint, got unexpected response ' .
203
+ $result['body'] );
204
+ }
205
+
206
+ if ( isset( $response_json['error'] ) ) {
207
+ if ( isset( $response_json['code'] ) && $response_json['code'] == '203' )
208
+ return array( 'response_json' => $response_json, 'auth_required' => true );
209
+
210
+ throw new \Exception( $response_json['error'] );
211
+ }
212
+
213
+ if ( $result['response']['code'] != '200' && $result['response']['code'] != '201' )
214
+ throw new \Exception( $result['body'] );
215
+
216
+ return array( 'response_json' => $response_json, 'auth_required' => false );
217
+ }
218
+ }
Cdn_Highwinds_Page.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_Highwinds_Page {
5
+ // called from plugin-admin
6
+ static public function admin_print_scripts_w3tc_cdn() {
7
+ wp_enqueue_script( 'w3tc_cdn_highwinds',
8
+ plugins_url( 'Cdn_Highwinds_Page_View.js', W3TC_FILE ),
9
+ array( 'jquery' ), '1.0' );
10
+ }
11
+
12
+
13
+
14
+ static public function w3tc_settings_cdn_boxarea_configuration() {
15
+ $config = Dispatcher::config();
16
+ include W3TC_DIR . '/Cdn_Highwinds_Page_View.php';
17
+ }
18
+ }
Cdn_Highwinds_Page_View.js ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tchw_resize(o) {
3
+ o.options.height = jQuery('.w3tc_cdn_highwinds_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+ $('body')
8
+ .on('click', '.w3tc_cdn_highwinds_authorize', function() {
9
+ W3tc_Lightbox.open({
10
+ id:'w3tc-overlay',
11
+ close: '',
12
+ width: 800,
13
+ height: 300,
14
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
15
+ '&w3tc_action=cdn_highwinds_authenticate',
16
+ callback: w3tchw_resize
17
+ });
18
+ })
19
+
20
+
21
+
22
+ .on('click', '.w3tc_cdn_highwinds_select_host', function() {
23
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
24
+ '&w3tc_action=cdn_highwinds_select_host';
25
+
26
+ var v = $('.w3tc_cdn_highwinds_form').find('input').each(function(i) {
27
+ var name = $(this).attr('name');
28
+ if (name)
29
+ url += '&' + encodeURIComponent(name) + '=' +
30
+ encodeURIComponent($(this).val());
31
+ });
32
+
33
+ W3tc_Lightbox.load(url, w3tchw_resize);
34
+ })
35
+
36
+
37
+
38
+ .on('click', '.w3tc_cdn_highwinds_configure_host', function() {
39
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
40
+ '&w3tc_action=cdn_highwinds_configure_host';
41
+
42
+ var v = $('.w3tc_cdn_highwinds_form').find('input').each(function(i) {
43
+ var name = $(this).attr('name');
44
+ var type = $(this).attr('type');
45
+ if (type == 'radio') {
46
+ if (!$(this).attr('checked'))
47
+ return;
48
+ }
49
+
50
+ if (name)
51
+ url += '&' + encodeURIComponent(name) + '=' +
52
+ encodeURIComponent($(this).val());
53
+ });
54
+
55
+ W3tc_Lightbox.load(url, w3tchw_resize);
56
+ })
57
+
58
+
59
+
60
+ .on('click', '.w3tc_cdn_highwinds_configure_cnames_form', function() {
61
+ W3tc_Lightbox.open({
62
+ id:'w3tc-overlay',
63
+ close: '',
64
+ width: 1000,
65
+ height: 400,
66
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
67
+ '&w3tc_action=cdn_highwinds_configure_cnames_form',
68
+ callback: function(o) {
69
+ w3tchw_resize(o);
70
+ w3tc_cdn_cnames_assign();
71
+ }
72
+ });
73
+ })
74
+
75
+
76
+
77
+ .on('click', '.w3tc_cdn_highwinds_configure_cnames', function() {
78
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
79
+ '&w3tc_action=cdn_highwinds_configure_cnames';
80
+
81
+ var v = $('.w3tc_cdn_highwinds_form').find('input').each(function(i) {
82
+ var name = $(this).attr('name');
83
+
84
+ if (name)
85
+ url += '&' + encodeURIComponent(name) + '=' +
86
+ encodeURIComponent($(this).val());
87
+ });
88
+
89
+ W3tc_Lightbox.load(url, function(o) {
90
+ w3tchw_resize(o);
91
+ w3tc_cdn_cnames_assign();
92
+ });
93
+ })
94
+
95
+
96
+
97
+ .on('size_change', '#cdn_cname_add', function() {
98
+ w3tchw_resize(W3tc_Lightbox);
99
+ })
100
+ });
Cdn_Highwinds_Page_View.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ $hash_code = $config->get_string( 'cdn.highwinds.host.hash_code' );
8
+ ?>
9
+ <tr>
10
+ <th style="width: 300px;"><label><?php _e( 'Authorize:', 'w3-total-cache' ); ?></label></th>
11
+ <td>
12
+ <?php if ( empty( $hash_code ) ): ?>
13
+ <input class="w3tc_cdn_highwinds_authorize button" type="button"
14
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>" />
15
+ <?php else: ?>
16
+ <input class="w3tc_cdn_highwinds_authorize button" type="button"
17
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>" />
18
+ <?php endif ?>
19
+ </td>
20
+ </tr>
21
+
22
+ <?php if ( !empty( $hash_code ) ): ?>
23
+ <tr>
24
+ <th><label><?php _e( 'CDN host (CNAME target):', 'w3-total-cache' ); ?></label></th>
25
+ <td class="w3tc_config_value_text">
26
+ cds.<?php echo $config->get_string( 'cdn.highwinds.host.hash_code' ) ?>.hwcdn.net
27
+ </td>
28
+ </tr>
29
+ <tr>
30
+ <th><label for="cdn_highwinds_ssl"><?php _e( '<acronym title="Secure Sockets Layer">SSL</acronym> support:</label>', 'w3-total-cache' ); ?></th>
31
+ <td>
32
+ <select id="cdn_highwinds_ssl" name="cdn__highwinds__ssl">
33
+ <option value="auto"<?php selected( $config->get_string( 'cdn.highwinds.ssl' ), 'auto' ); ?>><?php _e( 'Auto (determine connection type automatically)', 'w3-total-cache' ); ?></option>
34
+ <option value="enabled"<?php selected( $config->get_string( 'cdn.highwinds.ssl' ), 'enabled' ); ?>><?php _e( 'Enabled (always use SSL)', 'w3-total-cache' ); ?></option>
35
+ <option value="disabled"<?php selected( $config->get_string( 'cdn.highwinds.ssl' ), 'disabled' ); ?>><?php _e( 'Disabled (always use HTTP)', 'w3-total-cache' ); ?></option>
36
+ </select>
37
+ <br /><span class="description"><?php _e( 'Some <acronym>CDN</acronym> providers may or may not support <acronym title="Secure Sockets Layer">SSL</acronym>, contact your vendor for more information.', 'w3-total-cache' ); ?></span>
38
+ </td>
39
+ </tr>
40
+ <tr>
41
+ <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
42
+ <td>
43
+ <?php $cnames = $config->get_array( 'cdn.highwinds.host.domains' ); include W3TC_INC_DIR . '/options/cdn/common/cnames-readonly.php'; ?>
44
+ <input class="w3tc_cdn_highwinds_configure_cnames_form button" type="button"
45
+ value="<?php _e( 'Configure CNAMEs', 'w3-total-cache' ); ?>" />
46
+ <br />
47
+ <span class="description"><?php _e( 'Hostname provided by your <acronym>CDN</acronym> provider, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>.', 'w3-total-cache' ); ?></span>
48
+ </td>
49
+ </tr>
50
+ <tr>
51
+ <th colspan="2">
52
+ <input id="cdn_test"
53
+ class="button {type: 'highwinds', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}"
54
+ type="button"
55
+ value="<?php _e( 'Test', 'w3-total-cache' ); ?>" />
56
+ <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
57
+ </th>
58
+ </tr>
59
+ <?php endif ?>
Cdn_Highwinds_Popup.php ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_Highwinds_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Cdn_Highwinds_Popup();
9
+
10
+ add_action( 'w3tc_ajax_cdn_highwinds_authenticate',
11
+ array( $o, 'w3tc_ajax_cdn_highwinds_authenticate' ) );
12
+ add_action( 'w3tc_ajax_cdn_highwinds_select_host',
13
+ array( $o, 'w3tc_ajax_cdn_highwinds_select_host' ) );
14
+ add_action( 'w3tc_ajax_cdn_highwinds_configure_host',
15
+ array( $o, 'w3tc_ajax_cdn_highwinds_configure_host' ) );
16
+ add_action( 'w3tc_ajax_cdn_highwinds_configure_cnames_form',
17
+ array( $o, 'w3tc_ajax_cdn_highwinds_configure_cnames_form' ) );
18
+ add_action( 'w3tc_ajax_cdn_highwinds_configure_cnames',
19
+ array( $o, 'w3tc_ajax_cdn_highwinds_configure_cnames' ) );
20
+ }
21
+
22
+
23
+
24
+ public function w3tc_ajax_cdn_highwinds_authenticate() {
25
+ $details = array();
26
+ include W3TC_DIR . '/Cdn_Highwinds_Popup_View_Intro.php';
27
+ exit();
28
+ }
29
+
30
+
31
+
32
+ public function w3tc_ajax_cdn_highwinds_select_host() {
33
+ $api_token = $_REQUEST['api_token'];
34
+
35
+ try {
36
+ $user = Cdn_Highwinds_Api::users_me( $api_token );
37
+ $account_hash = $user['accountHash'];
38
+
39
+ // obtain hosts
40
+ $api = new Cdn_Highwinds_Api( $account_hash, $api_token );
41
+ $hosts_response = $api->hosts();
42
+ } catch ( \Exception $ex ) {
43
+ $details = array(
44
+ 'error_message' => 'Can\'t authenticate: ' . $ex->getMessage()
45
+ );
46
+ include W3TC_DIR . '/Cdn_Highwinds_Popup_View_Intro.php';
47
+ exit();
48
+ }
49
+
50
+ $details = array(
51
+ 'account_hash' => $account_hash,
52
+ 'api_token' => $api_token,
53
+ 'hosts' => $hosts_response['list']
54
+ );
55
+
56
+ include W3TC_DIR . '/Cdn_Highwinds_Popup_View_SelectHost.php';
57
+ exit();
58
+ }
59
+
60
+
61
+
62
+ public function w3tc_ajax_cdn_highwinds_configure_host() {
63
+ $account_hash = $_REQUEST['account_hash'];
64
+ $api_token = $_REQUEST['api_token'];
65
+
66
+ $host = Util_Request::get( 'host', '' );
67
+
68
+ $details = array(
69
+ 'account_hash' => $account_hash,
70
+ 'api_token' => $api_token
71
+ );
72
+
73
+ $api = new Cdn_Highwinds_Api( $account_hash, $api_token );
74
+
75
+ try {
76
+ if ( empty( $host ) ) {
77
+ $host = $this->_create_host( $api, $_REQUEST['host_new'] );
78
+ }
79
+ } catch ( \Exception $ex ) {
80
+ $api_hosts = $api->hosts();
81
+ $details['hosts'] = $api_hosts['list'];
82
+ $details['error_message'] = $ex->getMessage();
83
+ include W3TC_DIR . '/Cdn_Highwinds_Popup_View_SelectHost.php';
84
+ exit();
85
+ }
86
+
87
+ // try to obtain CNAMEs
88
+ $c = Dispatcher::config();
89
+ try {
90
+ $scopes_response = $api->configure_scopes( $host );
91
+ $scope_id = 0;
92
+
93
+ foreach ( $scopes_response['list'] as $scope ) {
94
+ if ( $scope['platform'] == 'CDS' )
95
+ $scope_id = $scope['id'];
96
+ }
97
+
98
+ if ( $scope_id <= 0 )
99
+ throw new Exception( 'scope CDN hasnt been created' );
100
+
101
+ $configuration = $api->configure_scope_get( $host, $scope_id );
102
+ if ( isset( $configuration['hostname'] ) ) {
103
+ $domains = array();
104
+ foreach ( $configuration['hostname'] as $d )
105
+ $domains[] = $d['domain'];
106
+
107
+ $c->set( 'cdn.highwinds.host.domains', $domains );
108
+ }
109
+ } catch ( \Exception $ex ) {
110
+ }
111
+
112
+ $c->set( 'cdn.highwinds.account_hash', $account_hash );
113
+ $c->set( 'cdn.highwinds.api_token', $api_token );
114
+ $c->set( 'cdn.highwinds.host.hash_code', $host );
115
+ $c->save();
116
+
117
+ $postfix = Util_Admin::custom_message_id( array(),
118
+ array(
119
+ 'cdn_configuration_saved' =>
120
+ 'CDN credentials are saved successfully' ) );
121
+ echo 'Location admin.php?page=w3tc_cdn&' . $postfix;
122
+ exit();
123
+ }
124
+
125
+
126
+
127
+ private function _create_host( $api, $host_name ) {
128
+ // create simple host
129
+ $services_response = $api->services();
130
+
131
+ // select all CDS services since its going to use caching
132
+ $service_ids = array();
133
+ foreach ( $services_response['list'] as $s ) {
134
+ if ( strpos( $s['name'], 'CDS' ) >= 0 )
135
+ $service_ids[] = $s['id'];
136
+ }
137
+
138
+ $origins_response = $api->origins();
139
+ $home_domain = Util_Environment::home_url_host();
140
+ $origin_id = 0;
141
+ foreach ( $origins_response['list'] as $o ) {
142
+ if ( $o['hostname'] == $home_domain ) {
143
+ $origin_id = $o['id'];
144
+ break;
145
+ }
146
+ }
147
+
148
+ if ( $origin_id == 0 ) {
149
+ try {
150
+ $name = preg_replace( '/[^0-9a-z]/', '_', $home_domain );
151
+
152
+ $origin_response = $api->origin_add( array(
153
+ 'name' => $name,
154
+ 'hostname' => $home_domain,
155
+ 'path' => '/',
156
+ 'port' => 80
157
+ ) );
158
+
159
+ $origin_id = $origin_response['id'];
160
+ } catch ( \Exception $ex ) {
161
+ throw new \Exception( 'Can\'t create origin ' . $home_domain . ': ' .
162
+ $ex->getMessage() );
163
+ }
164
+ }
165
+
166
+ try {
167
+ // create host
168
+ $host_response = $api->host_add( array(
169
+ 'name' => $_REQUEST['host_new'],
170
+ 'services' => $service_ids
171
+ ) );
172
+ $host = $host_response['hashCode'];
173
+ } catch ( \Exception $ex ) {
174
+ throw new \Exception( 'Can\'t create new host: ' . $ex->getMessage() );
175
+ }
176
+
177
+
178
+ // configure host
179
+ $scopes_response = $api->configure_scopes( $host );
180
+ $scope_id = 0;
181
+
182
+ foreach ( $scopes_response['list'] as $scope ) {
183
+ if ( $scope['platform'] == 'CDS' )
184
+ $scope_id = $scope['id'];
185
+ }
186
+
187
+ if ( $scope_id <= 0 )
188
+ throw new Exception( 'Cant\'t configure host - scope CDN hasnt been created' );
189
+
190
+ $configuration = $api->configure_scope_get( $host, $scope_id );
191
+
192
+ // apply usually optimal default values
193
+ $configuration['cacheControl'] = array( array( 'maxAge' => 31536000 ) );
194
+ $configuration['compression'] = array( 'gzip' => 'css,js' );
195
+ $configuration['originPullCacheExtension'] = array(
196
+ 'expiredCacheExtension' => 86400 );
197
+ $configuration['originPullHost'] = array( 'primary' => $origin_id );
198
+ $configuration['originPullPolicy'] = array( array(
199
+ 'expirePolicy' => 'CACHE_CONTROL',
200
+ 'expireSeconds' => 86400,
201
+ 'httpHeaders' => 'Access-Control-Allow-Origin'
202
+ ) );
203
+
204
+ try {
205
+ $configuration_response = $api->configure_scope_set( $host,
206
+ $scope_id, $configuration );
207
+ } catch ( \Exception $ex ) {
208
+ throw new \Exception( 'Cant\'t configure host: ' . $ex->getMessage() );
209
+ }
210
+
211
+ return $host;
212
+ }
213
+
214
+
215
+
216
+ public function w3tc_ajax_cdn_highwinds_configure_cnames_form() {
217
+ $this->render_configure_cnames_form();
218
+ exit();
219
+ }
220
+
221
+
222
+
223
+ public function w3tc_ajax_cdn_highwinds_configure_cnames() {
224
+ $details = array(
225
+ 'cnames' => Util_Request::get_array( 'cdn_cnames' )
226
+ );
227
+
228
+ $core = Dispatcher::component( 'Cdn_Core' );
229
+ $cdn = $core->get_cdn();
230
+
231
+ try {
232
+ // try to obtain CNAMEs
233
+ $cdn->service_cnames_set( $details['cnames'] );
234
+
235
+ $c = Dispatcher::config();
236
+ $c->set( 'cdn.highwinds.host.domains', $details['cnames'] );
237
+ $c->save();
238
+
239
+ $postfix = Util_Admin::custom_message_id( array(),
240
+ array( 'cdn_cnames_saved' => 'CNAMEs are saved successfully' ) );
241
+ echo 'Location admin.php?page=w3tc_cdn&' . $postfix;
242
+ exit();
243
+ } catch ( \Exception $ex ) {
244
+ $details['error_message'] = $ex->getMessage();
245
+ }
246
+
247
+ $this->render_configure_cnames_form( $details );
248
+ exit();
249
+ }
250
+
251
+
252
+
253
+ private function render_configure_cnames_form( $details = array() ) {
254
+ if ( isset( $details['cnames'] ) )
255
+ $cnames = $details['cnames'];
256
+ else {
257
+ $core = Dispatcher::component( 'Cdn_Core' );
258
+ $cdn = $core->get_cdn();
259
+
260
+ try {
261
+ // try to obtain CNAMEs
262
+ $cnames = $cdn->service_cnames_get();
263
+ } catch ( \Exception $ex ) {
264
+ $details['error_message'] = $ex->getMessage();
265
+ $cnames = array();
266
+ }
267
+ }
268
+
269
+ include W3TC_DIR . '/Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php';
270
+ }
271
+ }
Cdn_Highwinds_Popup_View_ConfigureCnamesForm.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px" class="w3tc_cdn_highwinds_form">
8
+ <?php
9
+ if ( !empty( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header( __( 'CNAMEs to use', 'w3-total-cache' ) ); ?>
14
+ <?php $cname_class = 'w3tc-ignore-change'; include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
15
+ <br />
16
+ <span class="description"><?php _e( 'Enter hostname mapped to <acronym>CDN</acronym> host, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>.', 'w3-total-cache' ); ?></span>
17
+
18
+ <p class="submit">
19
+ <input type="button"
20
+ class="w3tc_cdn_highwinds_configure_cnames w3tc-button-save button-primary"
21
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
22
+ </p>
23
+ <?php Util_Ui::postbox_footer(); ?>
24
+ </div>
25
+ </form>
Cdn_Highwinds_Popup_View_Intro.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_cdn_highwinds_form" method="post" style="padding: 20px">
8
+ <?php
9
+ if ( isset( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header(
14
+ __( 'Your Highwinds API Token', 'w3-total-cache' ) ); ?>
15
+ <table class="form-table">
16
+ <tr>
17
+ <td>API Token:</td>
18
+ <td>
19
+ <input name="api_token" type="text" class="w3tc-ignore-change"
20
+ value="" style="width: 550px" />
21
+ </td>
22
+ </tr>
23
+ </table>
24
+
25
+ <p class="submit">
26
+ <input type="button"
27
+ class="w3tc_cdn_highwinds_select_host w3tc-button-save button-primary"
28
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
29
+ </p>
30
+ <?php Util_Ui::postbox_footer(); ?>
31
+ </div>
32
+ </form>
Cdn_Highwinds_Popup_View_SelectHost.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_highwinds_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'account_hash', $details['account_hash'] );
11
+ Util_Ui::hidden( '', 'api_token', $details['api_token'] );
12
+ echo Util_Ui::nonce_field( 'w3tc' );
13
+
14
+ ?>
15
+ <?php
16
+ if ( isset( $details['error_message'] ) )
17
+ echo '<div class="error">' . $details['error_message'] . '</div>';
18
+ ?>
19
+ <div class="metabox-holder">
20
+ <?php Util_Ui::postbox_header( __( 'Select host to use', 'w3-total-cache' ) ); ?>
21
+ <table class="form-table">
22
+ <tr>
23
+ <td>Host:</td>
24
+ <td>
25
+ <?php foreach ( $details['hosts'] as $host ): ?>
26
+ <label>
27
+ <input name="host" type="radio" class="w3tc-ignore-change"
28
+ value="<?php echo $host['hashCode'] ?>" />
29
+ <?php echo $host['name'] ?>
30
+ (<?php echo $host['hashCode'] ?>)
31
+ </label><br />
32
+ <?php endforeach ?>
33
+
34
+ <label>
35
+ <input name="host" type="radio" class="w3tc-ignore-change" value=""
36
+ />
37
+ Add new host:
38
+ </label>
39
+ <input name="host_new" type="text" class="w3tc-ignore-change" />
40
+ </tr>
41
+ </table>
42
+
43
+ <p class="submit">
44
+ <input type="button"
45
+ class="w3tc_cdn_highwinds_configure_host w3tc-button-save button-primary"
46
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
47
+ </p>
48
+ <?php Util_Ui::postbox_footer(); ?>
49
+ </div>
50
+ </form>
Cdn_Highwinds_Widget.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_Highwinds_Widget {
5
+ static public function admin_init_w3tc_dashboard() {
6
+ $o = new Cdn_Highwinds_Widget();
7
+ add_action( 'admin_print_styles',
8
+ array( $o, 'admin_print_styles' ) );
9
+ add_action( 'admin_print_scripts',
10
+ array( $o, 'admin_print_scripts' ) );
11
+ add_action( 'w3tc_widget_setup',
12
+ array( $o, 'w3tc_widget_setup' ) );
13
+ }
14
+
15
+
16
+
17
+ public function w3tc_widget_setup() {
18
+ Util_Widget::add( 'w3tc_highwinds',
19
+ '<div class="w3tc-widget-highwinds-logo"></div>',
20
+ array( $this, 'widget_form' ),
21
+ Util_Ui::admin_url( 'admin.php?page=w3tc_cdn' ),
22
+ 'normal' );
23
+ }
24
+
25
+
26
+
27
+ public function widget_form() {
28
+ $c = Dispatcher::config();
29
+ $account_hash = $c->get_string( 'cdn.highwinds.account_hash' );
30
+ if ( empty( $account_hash ) ) {
31
+ include W3TC_DIR . '/Cdn_Highwinds_Widget_View_NotConfigured.php';
32
+ return;
33
+ }
34
+
35
+ $url_manage = 'https://striketracker3.highwinds.com/accounts/' .
36
+ $account_hash . '/configure/hosts';
37
+ $url_analyze = 'https://striketracker3.highwinds.com/accounts/' .
38
+ $account_hash . '/analyze/overview';
39
+ $url_purge = Util_Ui::url( array(
40
+ 'page' => 'w3tc_cdn',
41
+ 'w3tc_cdn_purge' => 'y'
42
+ ) );
43
+
44
+ include W3TC_DIR . '/Cdn_Highwinds_Widget_View.php';
45
+ }
46
+
47
+
48
+
49
+
50
+ public function admin_print_styles() {
51
+ wp_enqueue_style( 'w3tc-widget' );
52
+ wp_enqueue_style( 'w3tc-highwinds-widget',
53
+ plugins_url( 'Cdn_Highwinds_Widget_View.css', W3TC_FILE ),
54
+ array(), W3TC_VERSION );
55
+ }
56
+
57
+
58
+
59
+ public function admin_print_scripts() {
60
+ wp_enqueue_script( 'google-jsapi', 'https://www.google.com/jsapi' );
61
+ wp_enqueue_script( 'w3tc-highwinds-widget',
62
+ plugins_url( 'Cdn_Highwinds_Widget_View.js', W3TC_FILE ),
63
+ array(), W3TC_VERSION );
64
+ }
65
+
66
+
67
+
68
+ static public function w3tc_ajax_cdn_highwinds_widgetdata() {
69
+ try {
70
+ $core = Dispatcher::component( 'Cdn_Core' );
71
+ $cdn = $core->get_cdn();
72
+
73
+ $analytics = $cdn->service_analytics_transfer();
74
+
75
+ $sum_mbytes = 0;
76
+ $sum_mbps = 0;
77
+ $sum_rps = 0;
78
+ $graph = array( array( 'Date', 'Requests' ) );
79
+ $count = count( $analytics );
80
+
81
+ foreach ( $analytics as $item ) {
82
+ $sum_mbytes += $item['xferUsedTotalMB'];
83
+ $sum_mbps += $item['xferRateMeanMbps'];
84
+ $sum_rps += $item['rpsMean'];
85
+ $graph[] = array(
86
+ gmdate( 'd M', $item['usageTime'] / 1000 ),
87
+ $item['requestsCountTotal']
88
+ );
89
+ }
90
+
91
+ $response = array(
92
+ 'transferred_size' => Util_Ui::format_mbytes( $sum_mbytes / $count ),
93
+ 'average_mbps' => sprintf( '%.2f', $sum_mbps / $count ),
94
+ 'average_rps' => sprintf( '%.2f', $sum_rps / $count ),
95
+ 'graph' => $graph
96
+ );
97
+
98
+ echo json_encode( $response );
99
+ } catch ( \Exception $e ) {
100
+ echo json_encode( array(
101
+ 'error' => $e->getMessage()
102
+ ) );
103
+ }
104
+ }
105
+ }
Cdn_Highwinds_Widget_View.css ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .w3tc-widget-highwinds-logo {
2
+ float: left;
3
+ width: 65px;
4
+ height: 35px;
5
+ background: url("pub/img/cdn-highwinds-logo.png") 0 0 no-repeat;
6
+ }
7
+
8
+ .w3tchw_tools li {
9
+ display: inline-block;
10
+ margin-right: 10px;
11
+ }
12
+
13
+ #w3tchw_report li {
14
+ margin-bottom: 0;
15
+ }
Cdn_Highwinds_Widget_View.js ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var w3tchw_graph_data;
2
+
3
+ function w3tchw_load() {
4
+ jQuery('.w3tchw_loading').removeClass('w3tc_hidden');
5
+ jQuery('.w3tchw_content').addClass('w3tc_hidden');
6
+ jQuery('.w3tchw_error').addClass('w3tc_none');
7
+
8
+ jQuery.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
9
+ '&w3tc_action=cdn_highwinds_widgetdata',
10
+ function(data) {
11
+ if (data && data.error) {
12
+ jQuery('.w3tchw_error').removeClass('w3tc_none');
13
+ jQuery('.w3tchw_error_details').html(data.error);
14
+ jQuery('.w3tchw_loading').addClass('w3tc_hidden');
15
+ return;
16
+ }
17
+
18
+ for (p in data) {
19
+ var v = data[p];
20
+ jQuery('.w3tchw_' + p).html(v);
21
+ }
22
+
23
+ var data = google.visualization.arrayToDataTable(data.graph);
24
+ var options = {
25
+ legend: { position: "none" },
26
+ bars: 'horizontal'
27
+ };
28
+
29
+ var chart = new google.charts.Bar(document.getElementById('w3tchw_chart'));
30
+ chart.draw(data, options);
31
+
32
+ jQuery('.w3tchw_content').removeClass('w3tc_hidden');
33
+ jQuery('.w3tchw_loading').addClass('w3tc_hidden');
34
+ }
35
+ ).fail(function() {
36
+ jQuery('.w3tchw_error').removeClass('w3tc_none');
37
+ jQuery('.w3tchw_content').addClass('w3tc_hidden');
38
+ jQuery('.w3tchw_loading').addClass('w3tc_hidden');
39
+ });
40
+ }
41
+
42
+
43
+
44
+ google.load("visualization", "1.1", {packages:["bar"]});
45
+ google.setOnLoadCallback(w3tchw_load);
Cdn_Highwinds_Widget_View.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <div class="wrapper">
9
+ <div class="tools area">
10
+ <ul class="w3tchw_tools">
11
+ <li><a class="button"
12
+ href="<?php echo $url_manage ?>"><?php _e( 'Manage', 'w3-total-cache' )?></a>
13
+ </li>
14
+ <li><a class="button"
15
+ href="<?php echo $url_analyze ?>"><?php _e( 'Reports', 'w3-total-cache' )?></a>
16
+ </li>
17
+ <li><a class="button" href="<?php echo $url_purge ?>"
18
+ onclick="w3tc_popupadmin_bar(this.href); return false"><?php _e( 'Purge', 'w3-total-cache' )?></a>
19
+ </li>
20
+ </ul>
21
+ </div>
22
+ <div class="w3tchw_loading w3tc_loading w3tc_hidden">Loading...</div>
23
+ <div class="w3tchw_error w3tc_none">
24
+ An error occurred
25
+ <div class="w3tchw_error_details"></div>
26
+ </div>
27
+
28
+ <div class="w3tchw_content w3tc_hidden">
29
+ <div class="summary area">
30
+ <h4><?php _e( 'Report - 30 days', 'w3-total-cache' ) ?></h4>
31
+ <ul id="w3tchw_report">
32
+ <li>Transferred: <span class="w3tchw_transferred_size"></span></li>
33
+ <li>Average rate Mb/s: <span class="w3tchw_average_mbps"></span></li>
34
+ <li>Average requests/s: <span class="w3tchw_average_rps"></span></li>
35
+ </ul>
36
+ </div>
37
+ <div class="charts area">
38
+ <h4><?php _e( 'Requests', 'w3-total-cache' ) ?></h4>
39
+ <div id="w3tchw_chart" style="width: 320px; height: 220px;margin-left: auto; margin-right: auto;"></div>
40
+ </div>
41
+ </div>
42
+ </div>
Cdn_Highwinds_Widget_View_NotConfigured.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <div class="wrapper">
9
+ Not configured
10
+ </div>
Cdn_MaxCdnFsd_Engine.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_MaxCdnFsd_Engine {
7
+ private $api_key;
8
+ private $zone_id;
9
+
10
+
11
+
12
+ function __construct( $config = array() ) {
13
+ $this->api_key = $config['api_key'];
14
+ $this->zone_id = $config['zone_id'];
15
+ }
16
+
17
+
18
+
19
+ function flush_urls( $urls ) {
20
+ if ( empty( $this->api_key ) || empty( $this->zone_id ) )
21
+ throw new \Exception( __( 'API key not specified.', 'w3-total-cache' ) );
22
+
23
+ if ( !class_exists( 'NetDNA' ) )
24
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
25
+ $api = \NetDNA::create( $this->api_key );
26
+
27
+ $files = array();
28
+ foreach ( $urls as $url ) {
29
+ $parsed = parse_url( $url );
30
+ $relative_url =
31
+ ( isset( $parsed['path'] ) ? $parsed['path'] : '/' ) .
32
+ ( isset( $parsed['query'] ) ? '?' . $parsed['query'] : '' );
33
+ $files[] = $relative_url;
34
+ }
35
+
36
+ $api->cache_delete( $this->zone_id, $files );
37
+ }
38
+
39
+
40
+
41
+ /**
42
+ * Flushes CDN completely
43
+ */
44
+ function flush_all() {
45
+ if ( empty( $this->api_key ) || empty( $this->zone_id ) )
46
+ throw new \Exception( __( 'API key not specified.', 'w3-total-cache' ) );
47
+
48
+ if ( !class_exists( 'NetDNA' ) )
49
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
50
+ $api = \NetDNA::create( $this->api_key );
51
+
52
+ $api->cache_delete( $this->zone_id );
53
+ }
54
+ }
Cdn_MaxCdnFsd_Page.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_MaxCdnFsd_Page {
5
+ // called from plugin-admin
6
+ static public function admin_print_scripts_w3tc_cdn() {
7
+ wp_enqueue_script( 'w3tc_cdn_maxcdn_fsd',
8
+ plugins_url( 'Cdn_MaxCdnFsd_Page_View.js', W3TC_FILE ),
9
+ array( 'jquery' ), '1.0' );
10
+ }
11
+
12
+
13
+
14
+ static public function w3tc_settings_cdn() {
15
+ $config = Dispatcher::config();
16
+ include W3TC_DIR . '/Cdn_MaxCdnFsd_Page_View.php';
17
+ }
18
+ }
Cdn_MaxCdnFsd_Page_View.js ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tc_maxcdn_fsd_resize(o) {
3
+ o.options.height = jQuery('.w3tc_popup_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+ $('body')
8
+ .on('click', '.w3tc_cdn_maxcdn_fsd_authorize', function() {
9
+ W3tc_Lightbox.open({
10
+ id:'w3tc-overlay',
11
+ close: '',
12
+ width: 800,
13
+ height: 300,
14
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
15
+ '&w3tc_action=cdn_maxcdn_fsd_intro',
16
+ callback: w3tc_maxcdn_fsd_resize
17
+ });
18
+ })
19
+
20
+
21
+
22
+ .on('click', '.w3tc_cdn_maxcdn_fsd_list_zones', function() {
23
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
24
+ '&w3tc_action=cdn_maxcdn_fsd_list_zones';
25
+
26
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
27
+ var name = $(this).attr('name');
28
+ if (name)
29
+ url += '&' + encodeURIComponent(name) + '=' +
30
+ encodeURIComponent($(this).val());
31
+ });
32
+
33
+ W3tc_Lightbox.load(url, w3tc_maxcdn_fsd_resize);
34
+ })
35
+
36
+
37
+
38
+ .on('click', '.w3tc_cdn_maxcdn_fsd_view_zone', function() {
39
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
40
+ '&w3tc_action=cdn_maxcdn_fsd_view_zone';
41
+
42
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
43
+ var name = $(this).attr('name');
44
+ var type = $(this).attr('type');
45
+ if (type == 'radio') {
46
+ if (!$(this).attr('checked'))
47
+ return;
48
+ }
49
+
50
+ if (name)
51
+ url += '&' + encodeURIComponent(name) + '=' +
52
+ encodeURIComponent($(this).val());
53
+ });
54
+
55
+ W3tc_Lightbox.load(url, w3tc_maxcdn_fsd_resize);
56
+ })
57
+
58
+
59
+
60
+ .on('click', '.w3tc_cdn_maxcdn_fsd_configure_zone', function() {
61
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
62
+ '&w3tc_action=cdn_maxcdn_fsd_configure_zone';
63
+
64
+ var v = $('.w3tc_popup_form').find('input').each(function(i) {
65
+ var name = $(this).attr('name');
66
+ if (name)
67
+ url += '&' + encodeURIComponent(name) + '=' +
68
+ encodeURIComponent($(this).val());
69
+ });
70
+
71
+ W3tc_Lightbox.load(url, w3tc_maxcdn_fsd_resize);
72
+ })
73
+
74
+
75
+
76
+ .on('click', '.w3tc_cdn_maxcdn_fsd_done', function() {
77
+ // refresh page
78
+ window.location = window.location + '&';
79
+ })
80
+
81
+
82
+
83
+ .on('size_change', '#cdn_cname_add', function() {
84
+ w3tc_maxcdn_fsd_resize(W3tc_Lightbox);
85
+ })
86
+ });
Cdn_MaxCdnFsd_Page_View.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ $key = $config->get_string( 'cdn.maxcdn_fsd.api_key' );
8
+ $authorized = !empty( $key );
9
+
10
+ include W3TC_DIR . '/Cdn_Page_View_Header.php';
11
+ ?>
12
+ <p id="w3tc-options-menu">
13
+ <?php _e( 'Jump to:', 'w3-total-cache' ); ?>
14
+ <a href="?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a>
15
+ </p>
16
+ <?php
17
+ include W3TC_DIR . '/Cdn_Page_View_Fsd_HeaderActions.php';
18
+ ?>
19
+ <form id="cdn_form" action="admin.php?page=w3tc_cdn" method="post">
20
+ <div class="metabox-holder">
21
+ <?php Util_Ui::postbox_header( __( 'Configuration', 'w3-total-cache' ),
22
+ '', 'configuration' ); ?>
23
+ <table class="form-table">
24
+ <tr>
25
+ <th style="width: 300px;">
26
+ <label>
27
+ <?php
28
+ _e( 'Specify account credentials:',
29
+ 'w3-total-cache' );
30
+ ?>
31
+ </label>
32
+ </th>
33
+ <td>
34
+ <?php if ( $authorized ): ?>
35
+ <input class="w3tc_cdn_maxcdn_fsd_authorize button-primary"
36
+ type="button"
37
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>"
38
+ />
39
+ <?php else: ?>
40
+ <input class="w3tc_cdn_maxcdn_fsd_authorize button-primary"
41
+ type="button"
42
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>"
43
+ />
44
+ <?php endif ?>
45
+ </td>
46
+ </tr>
47
+
48
+ <?php if ( $authorized ): ?>
49
+ <tr>
50
+ <th>
51
+ <label><?php _e( 'CDN CNAME:', 'w3-total-cache' ); ?></label>
52
+ </th>
53
+ <td class="w3tc_config_value_text">
54
+ <?php
55
+ echo $config->get_string( 'cdn.maxcdn_fsd.zone_domain' )
56
+ ?><br />
57
+ <span class="description">
58
+ This website domain has to be CNAME pointing to this
59
+ CDN domain
60
+ </span>
61
+ </td>
62
+ </tr>
63
+ <?php endif ?>
64
+ </table>
65
+
66
+ <?php Util_Ui::button_config_save( 'cdn_configuration' ); ?>
67
+ <?php Util_Ui::postbox_footer(); ?>
68
+ </div>
69
+ </form>
70
+
71
+ <?php
72
+ include W3TC_INC_DIR . '/options/common/footer.php';
Cdn_MaxCdnFsd_Popup.php ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_MaxCdnFsd_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Cdn_MaxCdnFsd_Popup();
9
+
10
+ add_action( 'w3tc_ajax_cdn_maxcdn_fsd_intro',
11
+ array( $o, 'w3tc_ajax_cdn_maxcdn_fsd_intro' ) );
12
+ add_action( 'w3tc_ajax_cdn_maxcdn_fsd_list_zones',
13
+ array( $o, 'w3tc_ajax_cdn_maxcdn_fsd_list_zones' ) );
14
+ add_action( 'w3tc_ajax_cdn_maxcdn_fsd_view_zone',
15
+ array( $o, 'w3tc_ajax_cdn_maxcdn_fsd_view_zone' ) );
16
+ add_action( 'w3tc_ajax_cdn_maxcdn_fsd_configure_zone',
17
+ array( $o, 'w3tc_ajax_cdn_maxcdn_fsd_configure_zone' ) );
18
+ }
19
+
20
+
21
+
22
+ public function __construct() {
23
+ if ( !class_exists( 'NetDNA' ) )
24
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
25
+ }
26
+
27
+
28
+
29
+ public function w3tc_ajax_cdn_maxcdn_fsd_intro() {
30
+ $config = Dispatcher::config();
31
+
32
+ $this->render_intro( array(
33
+ 'api_key' => $config->get_string( 'cdn.maxcdn_fsd.api_key' ) ) );
34
+ }
35
+
36
+
37
+
38
+ private function render_intro( $details ) {
39
+ $config = Dispatcher::config();
40
+ $url_obtain_key = Util_Ui::url( array(
41
+ 'page' => 'w3tc_dashboard',
42
+ 'w3tc_cdn_maxcdn_authorize' => 'y'
43
+ ) );
44
+
45
+ include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Intro.php';
46
+ exit();
47
+ }
48
+
49
+
50
+
51
+ public function w3tc_ajax_cdn_maxcdn_fsd_list_zones() {
52
+ $api_key = $_REQUEST['api_key'];
53
+
54
+ $api = \NetDNA::create( $api_key );
55
+ if ( !$api->is_valid() ) {
56
+ $this->render_intro( array(
57
+ 'api_key' => $api_key,
58
+ 'error_message' => 'Can\'t authenticate: API key not valid'
59
+ ) );
60
+ exit();
61
+ }
62
+
63
+ try {
64
+ $zones = $api->get_pull_zones();
65
+ } catch ( \Exception $ex ) {
66
+ $error_message = 'Can\'t authenticate: ' . $ex->getMessage();
67
+
68
+ if ( strpos( $error_message, 'not whitelisted' ) > 0 ) {
69
+ $error_message .= '. You can whitelist IP ' .
70
+ '<a target="_blank" href="https://cp.maxcdn.com/account/api/whitelist">here</a>';
71
+ }
72
+ $this->render_intro( array(
73
+ 'api_key' => $api_key,
74
+ 'error_message' => $error_message
75
+ ) );
76
+ exit();
77
+ }
78
+
79
+ $details = array(
80
+ 'api_key' => $api_key,
81
+ 'zones' => $zones
82
+ );
83
+
84
+ include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Zones.php';
85
+ exit();
86
+ }
87
+
88
+
89
+
90
+ public function w3tc_ajax_cdn_maxcdn_fsd_view_zone() {
91
+ $api_key = $_REQUEST['api_key'];
92
+ $zone_id = Util_Request::get( 'zone_id', '' );
93
+
94
+ $details = array(
95
+ 'api_key' => $api_key,
96
+ 'zone_id' => $zone_id,
97
+ 'name' => '',
98
+ 'url' => array(
99
+ 'new' => get_home_url() ),
100
+ 'ip' => array(),
101
+ // needs to be off since original DNS will be replaced with maxcdn's
102
+ 'dns_check' => array(
103
+ 'new' => 0
104
+ ),
105
+ // needs to be off, since WP issues no-cache headers for wp-admin
106
+ // and logged-in users
107
+ 'ignore_cache_control' => array(
108
+ 'new' => 0
109
+ ),
110
+ 'custom_domain' => array(
111
+ 'new' => Util_Environment::home_url_host()
112
+ )
113
+ );
114
+
115
+ if ( empty( $zone_id ) ) {
116
+ // create new zone mode
117
+ $details['name'] = Util_Request::get( 'zone_new_name' );
118
+ $details['ip']['new'] = Cdn_Fsd_Util::get_suggested_home_ip();
119
+ } else {
120
+ $api = \NetDNA::create( $api_key );
121
+ try {
122
+ $zone = $api->get_zone( $zone_id );
123
+ $custom_domains = $api->get_custom_domains( $zone_id );
124
+ } catch ( \Exception $ex ) {
125
+ $this->render_intro( array(
126
+ 'api_key' => $api_key,
127
+ 'error_message' => 'Can\'t obtain zone: ' . $ex->getMessage()
128
+ ) );
129
+ exit();
130
+ }
131
+
132
+ $details['custom_domain']['current'] = '';
133
+
134
+ foreach ( $custom_domains as $d ) {
135
+ $details['custom_domain']['current'] = $d['custom_domain'];
136
+ if ( $d['custom_domain'] == Util_Environment::home_url_host() )
137
+ break;
138
+ }
139
+
140
+ $details['name'] = $zone['name'];
141
+ $details['dns_check']['current'] = $zone['dns_check'];
142
+ $details['ignore_cache_control'] = $zone['ignore_cache_control'];
143
+ $details['url']['current'] = $zone['url'];
144
+ $details['ip']['current'] = $zone['ip'];
145
+
146
+ $origin_ip = Cdn_Fsd_Util::get_suggested_home_ip();
147
+ $cdn_ip = gethostbyname( $zone['tmp_url'] );
148
+
149
+ if ( $origin_ip != $cdn_ip )
150
+ $details['ip']['new'] = $origin_ip;
151
+ }
152
+
153
+
154
+
155
+ include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Zone.php';
156
+ exit();
157
+ }
158
+
159
+
160
+
161
+ private function render_zone_value_change( $details, $field ) {
162
+ Util_Ui::hidden( '', $field, $details[$field]['new'] );
163
+
164
+ if ( !isset( $details[$field]['current'] ) ||
165
+ $details[$field]['current'] == $details[$field]['new'] )
166
+ echo htmlspecialchars( $details[$field]['new'] );
167
+ else {
168
+ echo 'currently set to <strong>' .
169
+ htmlspecialchars( empty( $details[$field]['current'] ) ?
170
+ '<empty>' : $details[$field]['current'] ) .
171
+ '</strong><br />';
172
+ echo 'will be changed to <strong>' .
173
+ htmlspecialchars( $details[$field]['new'] ) . '</strong><br />';
174
+ }
175
+ }
176
+
177
+
178
+
179
+ private function render_zone_boolean_change( $details, $field ) {
180
+ Util_Ui::hidden( '', $field, $details[$field]['new'] );
181
+
182
+ if ( !isset( $details[$field]['current'] ) ) {
183
+ echo 'will be set to <strong>';
184
+ echo $this->render_zone_boolean( $details[$field]['new'] );
185
+ echo '</strong>';
186
+ } else if ( $details[$field]['current'] == $details[$field]['new'] ) {
187
+ echo '<strong>';
188
+ echo $this->render_zone_boolean( $details[$field]['new'] );
189
+ echo '</strong>';
190
+ } else {
191
+ echo 'currently set to <strong>';
192
+ $this->render_zone_boolean( $details[$field]['current'] );
193
+ echo '</strong><br />';
194
+ echo 'will be changed to <strong>';
195
+ $this->render_zone_boolean( $details[$field]['new'] );
196
+ echo '</strong><br />';
197
+ }
198
+ }
199
+
200
+
201
+
202
+ private function render_zone_boolean( $v ) {
203
+ if ( $v == 0 )
204
+ echo 'disabled';
205
+ else
206
+ echo 'enabled';
207
+ }
208
+
209
+
210
+
211
+ private function render_zone_ip_change( $details, $field ) {
212
+ Util_Ui::textbox( '', $field, $details[$field]['new'] );
213
+
214
+ if ( isset( $details[$field]['current'] ) &&
215
+ $details[$field]['current'] != $details[$field]['new'] ) {
216
+ echo '<br /><span class="description">currently set to <strong>' .
217
+ $details[$field]['current'] . '</strong></span>';
218
+ }
219
+ }
220
+
221
+
222
+
223
+ public function w3tc_ajax_cdn_maxcdn_fsd_configure_zone() {
224
+ $api_key = $_REQUEST['api_key'];
225
+ $zone_id = Util_Request::get( 'zone_id', '' );
226
+
227
+ $zone = array(
228
+ 'name' => Util_Request::get( 'name' ),
229
+ 'label' => Util_Request::get( 'name' ),
230
+ 'url' => Util_Request::get( 'url' ),
231
+ 'use_stale' => 1,
232
+ 'queries' => 1,
233
+ 'compress' => 1,
234
+ 'backend_compress' => 1,
235
+ 'dns_check' => Util_Request::get( 'dns_check' ),
236
+ 'ip' => Util_Request::get( 'ip' )
237
+ );
238
+
239
+ $api = \NetDNA::create( $api_key );
240
+
241
+ try {
242
+ if ( empty( $zone_id ) ) {
243
+ $response = $api->create_pull_zone( $zone );
244
+ $zone_id = $response['id'];
245
+ } else {
246
+ $response = $api->update_pull_zone( $zone_id, $zone );
247
+ }
248
+
249
+ $custom_domains = $api->get_custom_domains( $zone_id );
250
+ $custom_domain = Util_Request::get( 'custom_domain' );
251
+
252
+ $added = false;
253
+ foreach ( $custom_domains as $d ) {
254
+ if ( $d['custom_domain'] == $custom_domain ) {
255
+ $added = true;
256
+ break;
257
+ }
258
+ }
259
+ if ( !$added ) {
260
+ $api->create_custom_domain( $zone_id, $custom_domain );
261
+ }
262
+ } catch ( \Exception $ex ) {
263
+ $this->render_intro( array(
264
+ 'api_key' => $api_key,
265
+ 'error_message' => 'Failed to configure custom domain ' . $custom_domain . ': ' . $ex->getMessage()
266
+ ) );
267
+ exit();
268
+ }
269
+
270
+ $zone_domain = $response['tmp_url'];
271
+
272
+ $c = Dispatcher::config();
273
+ $c->set( 'cdn.maxcdn_fsd.api_key', $api_key );
274
+ $c->set( 'cdn.maxcdn_fsd.zone_id', $zone_id );
275
+ $c->set( 'cdn.maxcdn_fsd.zone_domain', $zone_domain );
276
+ $c->save();
277
+
278
+ $details = array(
279
+ 'name' => $zone['name'],
280
+ 'home_domain' => Util_Environment::home_url_host(),
281
+ 'dns_cname_target' => $zone_domain,
282
+ );
283
+
284
+ include W3TC_DIR . '/Cdn_MaxCdnFsd_Popup_View_Success.php';
285
+ exit();
286
+ }
287
+ }
Cdn_MaxCdnFsd_Popup_View_Intro.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form">
8
+ <?php
9
+ if ( isset( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header(
14
+ __( 'Your MaxCDN Account credentials', 'w3-total-cache' ) ); ?>
15
+ <table class="form-table">
16
+ <tr>
17
+ <td>API Key:</td>
18
+ <td>
19
+ <input name="api_key" type="text" class="w3tc-ignore-change"
20
+ style="width: 550px"
21
+ value="<?php echo $details['api_key'] ?>" />
22
+ <br />
23
+ <span class="description">
24
+ To obtain API key you can
25
+ <a target="_blank" href="<?php echo $url_obtain_key ?>">click here</a>,
26
+ log in, and paste the key in above field.
27
+ </span>
28
+ </td>
29
+ </tr>
30
+ </table>
31
+
32
+ <p class="submit">
33
+ <input type="button"
34
+ class="w3tc_cdn_maxcdn_fsd_list_zones w3tc-button-save button-primary"
35
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
36
+ </p>
37
+ <?php Util_Ui::postbox_footer(); ?>
38
+ </div>
39
+ </form>
Cdn_MaxCdnFsd_Popup_View_Success.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form">
8
+ <div class="metabox-holder">
9
+ <?php Util_Ui::postbox_header(
10
+ __( 'Succeeded', 'w3-total-cache' ) ); ?>
11
+
12
+ <div style="text-align: center">
13
+ Pull Zone <?php echo $details['name'] ?> was successfully configured.<br />
14
+ Now you need to change DNS records of your domain
15
+ <strong><?php echo $details['home_domain'] ?></strong> and CNAME it to
16
+ <strong><?php echo $details['dns_cname_target'] ?></strong> to make caching work.
17
+ </div>
18
+
19
+ <p class="submit">
20
+ <input type="button"
21
+ class="w3tc_cdn_maxcdn_fsd_done w3tc-button-save button-primary"
22
+ value="<?php _e( 'Done', 'w3-total-cache' ); ?>" />
23
+ </p>
24
+ <?php Util_Ui::postbox_footer(); ?>
25
+ </div>
26
+ </form>
Cdn_MaxCdnFsd_Popup_View_Zone.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form" method="post">
8
+ <?php
9
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
10
+ Util_Ui::hidden( '', 'zone_id', $details['zone_id'] );
11
+ Util_Ui::hidden( '', 'name', $details['name'] );
12
+ ?>
13
+
14
+ <div class="metabox-holder">
15
+ <?php Util_Ui::postbox_header( __( 'Configure zone', 'w3-total-cache' ) ); ?>
16
+ <table class="form-table">
17
+ <tr>
18
+ <th>Name:</th>
19
+ <td><?php echo $details['name'] ?></td>
20
+ </tr>
21
+ <tr>
22
+ <th>Origin URL:</th>
23
+ <td><?php $this->render_zone_value_change( $details, 'url' ) ?></td>
24
+ </tr>
25
+ <tr>
26
+ <th>Origin IP:</th>
27
+ <td><?php $this->render_zone_ip_change( $details, 'ip' ) ?><br />
28
+ <span class="description">IP of your WordPress host</span>
29
+ </td>
30
+ </tr>
31
+ <tr>
32
+ <th>Origin IP Resolution:</th>
33
+ <td><?php $this->render_zone_boolean_change( $details, 'dns_check' ) ?></td>
34
+ </tr>
35
+ <tr>
36
+ <th>Ignore Cache Control:</th>
37
+ <td><?php $this->render_zone_boolean_change( $details, 'dns_check' ) ?></td>
38
+ </tr>
39
+ <tr>
40
+ <th>CDN Domain:</th>
41
+ <td>
42
+ <?php $this->render_zone_value_change( $details, 'custom_domain' ) ?><br />
43
+ <span class="description">Domain CDN will handle</span>
44
+ </td>
45
+ </tr>
46
+ </table>
47
+
48
+ <p class="submit">
49
+ <input type="button"
50
+ class="w3tc_cdn_maxcdn_fsd_configure_zone w3tc-button-save button-primary"
51
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
52
+ </p>
53
+ <?php Util_Ui::postbox_footer(); ?>
54
+ </div>
55
+ </form>
Cdn_MaxCdnFsd_Popup_View_Zones.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_popup_form" method="post">
8
+ <?php
9
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
10
+ ?>
11
+ <div class="metabox-holder">
12
+ <?php Util_Ui::postbox_header( __( 'Select zone to use', 'w3-total-cache' ) ); ?>
13
+ <table class="form-table">
14
+ <tr>
15
+ <td>Zone:</td>
16
+ <td>
17
+ <?php
18
+ if ( count( $details['zones'] ) > 15 )
19
+ echo '<div style="width: 100%; height: 300px; overflow-y: scroll">';
20
+ ?>
21
+
22
+ <?php foreach ( $details['zones'] as $zone ): ?>
23
+ <label>
24
+ <input name="zone_id" type="radio" class="w3tc-ignore-change"
25
+ value="<?php echo $zone['id'] ?>" />
26
+ <?php echo $zone['name'] ?>
27
+ (<?php echo $zone['cdn_url'] ?>)
28
+ </label><br />
29
+ <?php endforeach ?>
30
+
31
+ <label>
32
+ <input name="zone_id" type="radio" class="w3tc-ignore-change" value=""
33
+ />
34
+ Add new zone:
35
+ </label>
36
+ <input name="zone_new_name" type="text" class="w3tc-ignore-change" />
37
+
38
+ <?php
39
+ if ( count( $details['zones'] ) > 15 )
40
+ echo '</div>';
41
+ ?>
42
+ </td>
43
+ </tr>
44
+ </table>
45
+
46
+ <p class="submit">
47
+ <input type="button"
48
+ class="w3tc_cdn_maxcdn_fsd_view_zone w3tc-button-save button-primary"
49
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
50
+ </p>
51
+ <?php Util_Ui::postbox_footer(); ?>
52
+ </div>
53
+ </form>
Cdn_Page.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_Page extends Base_Page_Settings {
7
+ /**
8
+ * Current page
9
+ *
10
+ * @var string
11
+ */
12
+ protected $_page = 'w3tc_cdn';
13
+
14
+ /**
15
+ * CDN tab
16
+ *
17
+ * @return void
18
+ */
19
+ function view() {
20
+ $config = Dispatcher::config();
21
+ $cdn_engine = $config->get_string( 'cdn.engine' );
22
+
23
+ if ( Cdn_Util::is_engine_fsd( $cdn_engine ) ) {
24
+ do_action( 'w3tc_settings_cdn' );
25
+ return;
26
+ }
27
+
28
+ $cdn_enabled = $config->get_boolean( 'cdn.enabled' );
29
+ $cdn_mirror = Cdn_Util::is_engine_mirror( $cdn_engine );
30
+ $cdn_mirror_purge_all = Cdn_Util::can_purge_all( $cdn_engine );
31
+ $cdn_common = Dispatcher::component( 'Cdn_Core' );
32
+
33
+ $cdn = $cdn_common->get_cdn();
34
+ $cdn_supports_header = $cdn->headers_support() == W3TC_CDN_HEADER_MIRRORING;
35
+ $minify_enabled = (
36
+ $config->get_boolean( 'minify.enabled' ) &&
37
+ Util_Rule::can_check_rules() &&
38
+ $config->get_boolean( 'minify.rewrite' ) &&
39
+ ( !$config->get_boolean( 'minify.auto' ) ||
40
+ Cdn_Util::is_engine_mirror( $config->get_string( 'cdn.engine' ) ) ) );
41
+
42
+ $cookie_domain = $this->get_cookie_domain();
43
+ $set_cookie_domain = $this->is_cookie_domain_enabled();
44
+
45
+ // Required for Update Media Query String button
46
+ $browsercache_enabled = $config->get_boolean( 'browsercache.enabled' );
47
+ $browsercache_update_media_qs = ( $config->get_boolean( 'browsercache.cssjs.replace' ) || $config->get_boolean( 'browsercache.other.replace' ) );
48
+ if ( in_array( $cdn_engine, array( 'netdna', 'maxcdn' ) ) ) {
49
+ $pull_zones = array();
50
+ $authorization_key = $config->get_string( "cdn.$cdn_engine.authorization_key" );
51
+ $zone_id = $config->get_integer( "cdn.$cdn_engine.zone_id" );
52
+ $alias = $consumerkey = $consumersecret = '';
53
+
54
+ if ( $authorization_key ) {
55
+ $keys = explode( '+', $authorization_key );
56
+ if ( sizeof( $keys ) == 3 ) {
57
+ list( $alias, $consumerkey, $consumersecret ) = $keys;
58
+ }
59
+ }
60
+
61
+ $authorized = $authorization_key != '' && $alias && $consumerkey && $consumersecret;
62
+ $have_zone = $zone_id != 0;
63
+ if ( $authorized ) {
64
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
65
+ try {
66
+ $api = new \NetDNA( $alias, $consumerkey, $consumersecret );
67
+ $pull_zones = $api->get_zones_by_url( get_home_url() );
68
+ } catch ( \Exception $ex ) {
69
+
70
+
71
+ Util_Ui::error_box( '<p>There is an error with your CDN settings: ' . $ex->getMessage() . '</p>' );
72
+ }
73
+ }
74
+ }
75
+ include W3TC_INC_DIR . '/options/cdn.php';
76
+ }
77
+
78
+ /**
79
+ * Returns cookie domain
80
+ *
81
+ * @return string
82
+ */
83
+ function get_cookie_domain() {
84
+ $site_url = get_option( 'siteurl' );
85
+ $parse_url = @parse_url( $site_url );
86
+
87
+ if ( $parse_url && !empty( $parse_url['host'] ) ) {
88
+ return $parse_url['host'];
89
+ }
90
+
91
+ return $_SERVER['HTTP_HOST'];
92
+ }
93
+
94
+ /**
95
+ * Checks if COOKIE_DOMAIN is enabled
96
+ *
97
+ * @return bool
98
+ */
99
+ function is_cookie_domain_enabled() {
100
+ $cookie_domain = $this->get_cookie_domain();
101
+
102
+ return defined( 'COOKIE_DOMAIN' ) && COOKIE_DOMAIN == $cookie_domain;
103
+ }
104
+ }
Cdn_Page_View_Fsd_HeaderActions.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <p>
9
+ <?php
10
+ echo Util_Ui::button_link(
11
+ __( 'purge CDN completely', 'w3-total-cache' ),
12
+ Util_Ui::url( array( 'w3tc_cdn_flush' => 'y' ) ) );
13
+ ?>
14
+ </p>
Cdn_Page_View_Header.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php include W3TC_INC_DIR . '/options/common/header.php'; ?>
9
+
10
+ <p>
11
+ <?php echo sprintf(
12
+ __( 'Content Delivery Network support via %1$s is currently %2$s.', 'w3-total-cache' ),
13
+ '<strong>'.Cache::engine_name( $config->get_string( 'cdn.engine' ) ).'</strong>',
14
+ '<span class="w3tc-' . ( $config->get_boolean( 'cdn.enabled' ) ? 'enabled">' . __( 'enabled', 'w3-total-cache' ) : 'disabled">' . __( 'disabled', 'w3-total-cache' ) ) . '</span>'
15
+ ); ?>
16
+ </p>
Cdn_Plugin.php ADDED
@@ -0,0 +1,1162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
21
+ private $_replaced_urls = array();
22
+
23
+ function __construct() {
24
+ $this->_config = Dispatcher::config();
25
+ }
26
+
27
+ /**
28
+ * Runs plugin
29
+ */
30
+ function run() {
31
+ $cdn_engine = $this->_config->get_string( 'cdn.engine' );
32
+ if ( Cdn_Util::is_engine_fsd( $cdn_engine ) ) {
33
+ $this->run_fsd();
34
+ return;
35
+ }
36
+
37
+ add_filter( 'cron_schedules', array(
38
+ $this,
39
+ 'cron_schedules'
40
+ ) );
41
+
42
+ if ( !$this->_config->get_boolean( 'cdn.debug' ) )
43
+ add_filter( 'w3tc_footer_comment', array(
44
+ $this,
45
+ 'w3tc_footer_comment'
46
+ ) );
47
+
48
+ if ( !Cdn_Util::is_engine_mirror( $cdn_engine ) ) {
49
+ add_action( 'delete_attachment', array(
50
+ $this,
51
+ 'delete_attachment'
52
+ ) );
53
+
54
+ add_filter( 'update_attached_file', array(
55
+ $this,
56
+ 'update_attached_file'
57
+ ) );
58
+
59
+ add_filter( 'wp_update_attachment_metadata', array(
60
+ $this,
61
+ 'update_attachment_metadata'
62
+ ) );
63
+
64
+ add_action( 'w3_cdn_cron_queue_process', array(
65
+ $this,
66
+ 'cron_queue_process'
67
+ ) );
68
+
69
+ add_action( 'w3_cdn_cron_upload', array(
70
+ $this,
71
+ 'cron_upload'
72
+ ) );
73
+
74
+ add_action( 'switch_theme', array(
75
+ $this,
76
+ 'switch_theme'
77
+ ) );
78
+
79
+ add_filter( 'update_feedback', array(
80
+ $this,
81
+ 'update_feedback'
82
+ ) );
83
+ }
84
+
85
+ add_filter( 'w3tc_admin_bar_menu',
86
+ array( $this, 'w3tc_admin_bar_menu' ) );
87
+
88
+ if ( is_admin() ) {
89
+ add_action( 'w3tc_config_ui_save-w3tc_cdn', array(
90
+ $this, 'change_canonical_header' ), 0, 0 );
91
+ add_filter( 'w3tc_module_is_running-cdn', array( $this, 'cdn_is_running' ) );
92
+ }
93
+
94
+ /**
95
+ * Start rewrite engine
96
+ */
97
+ if ( $this->can_cdn() ) {
98
+ Util_Bus::add_ob_callback( 'cdn', array( $this, 'ob_callback' ) );
99
+ }
100
+
101
+ if ( is_admin() && Cdn_Util::can_purge( $cdn_engine ) ) {
102
+ add_filter( 'media_row_actions', array(
103
+ $this,
104
+ 'media_row_actions'
105
+ ), 0, 2 );
106
+ }
107
+ }
108
+
109
+ /**
110
+ * run code for FSD CDN
111
+ */
112
+ private function run_fsd() {
113
+ add_action( 'w3tc_flush_all', array(
114
+ '\W3TC\Cdn_Fsd_CacheFlush',
115
+ 'w3tc_flush_all'
116
+ ), 3000, 1 );
117
+ add_action( 'w3tc_flush_post', array(
118
+ '\W3TC\Cdn_Fsd_CacheFlush',
119
+ 'w3tc_flush_post'
120
+ ), 3000, 1 );
121
+ add_action( 'w3tc_flushable_posts', '__return_true', 3000 );
122
+ add_action( 'w3tc_flush_posts', array(
123
+ '\W3TC\Cdn_Fsd_CacheFlush',
124
+ 'w3tc_flush_all'
125
+ ), 3000 );
126
+ add_action( 'w3tc_flush_url', array(
127
+ '\W3TC\Cdn_Fsd_CacheFlush',
128
+ 'w3tc_flush_url'
129
+ ), 3000, 1 );
130
+ add_filter( 'w3tc_flush_execute_delayed_operations', array(
131
+ '\W3TC\Cdn_Fsd_CacheFlush',
132
+ 'w3tc_flush_execute_delayed_operations'
133
+ ), 3000 );
134
+
135
+ Util_AttachToActions::flush_posts_on_actions();
136
+ }
137
+
138
+ /**
139
+ * Instantiates worker with admin functionality on demand
140
+ *
141
+ * @return Cdn_Core_Admin
142
+ */
143
+ function get_admin() {
144
+ return Dispatcher::component( 'Cdn_Core_Admin' );
145
+ }
146
+
147
+ /**
148
+ * Cron queue process event
149
+ */
150
+ function cron_queue_process() {
151
+ $queue_limit = $this->_config->get_integer( 'cdn.queue.limit' );
152
+ return $this->get_admin()->queue_process( $queue_limit );
153
+ }
154
+
155
+ /**
156
+ * Cron upload event
157
+ */
158
+ function cron_upload() {
159
+ $files = $this->get_files();
160
+
161
+ $upload = array();
162
+ $results = array();
163
+
164
+ $common = Dispatcher::component( 'Cdn_Core' );
165
+
166
+ foreach ( $files as $file ) {
167
+ $local_path = $common->docroot_filename_to_absolute_path( $file );
168
+ $remote_path = $common->uri_to_cdn_uri( $common->docroot_filename_to_uri( $file ) );
169
+ $upload[] = $common->build_file_descriptor( $local_path, $remote_path );
170
+ }
171
+
172
+ $common->upload( $upload, true, $results );
173
+ }
174
+
175
+ /**
176
+ * Update attachment file
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
+ $common->upload( $files, true, $results );
191
+
192
+ return $attached_file;
193
+ }
194
+
195
+ /**
196
+ * On attachment delete action
197
+ *
198
+ * Delete _wp_attached_file, _wp_attachment_metadata, _wp_attachment_backup_sizes
199
+ *
200
+ * @param integer $attachment_id
201
+ */
202
+ function delete_attachment( $attachment_id ) {
203
+ $common = Dispatcher::component( 'Cdn_Core' );
204
+ $files = $common->get_attachment_files( $attachment_id );
205
+ $files = apply_filters( 'w3tc_cdn_delete_attachment', $files );
206
+
207
+ $results = array();
208
+
209
+ $common->delete( $files, true, $results );
210
+ }
211
+
212
+ /**
213
+ * Update attachment metadata filter
214
+ *
215
+ * Upload _wp_attachment_metadata
216
+ *
217
+ * @param array $metadata
218
+ * @return array
219
+ */
220
+ function update_attachment_metadata( $metadata ) {
221
+ $common = Dispatcher::component( 'Cdn_Core' );
222
+ $files = $common->get_metadata_files( $metadata );
223
+ $files = apply_filters( 'w3tc_cdn_update_attachment_metadata', $files );
224
+
225
+ $results = array();
226
+
227
+ $common->upload( $files, true, $results );
228
+
229
+ return $metadata;
230
+ }
231
+
232
+ /**
233
+ * Cron schedules filter
234
+ *
235
+ * @param array $schedules
236
+ * @return array
237
+ */
238
+ function cron_schedules( $schedules ) {
239
+ $c = $this->_config;
240
+
241
+ if ( $c->get_boolean( 'cdn.enabled' ) &&
242
+ !Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
243
+ $queue_interval = $c->get_integer( 'cdn.queue.interval' );
244
+ $schedules['w3_cdn_cron_queue_process'] = array(
245
+ 'interval' => $queue_interval,
246
+ 'display' => sprintf(
247
+ '[W3TC] CDN queue process (every %d seconds)', $queue_interval
248
+ )
249
+ );
250
+ }
251
+
252
+ if ( $c->get_boolean( 'cdn.enabled' ) &&
253
+ $c->get_boolean( 'cdn.autoupload.enabled' ) &&
254
+ !Cdn_Util::is_engine_mirror( $c->get_string( 'cdn.engine' ) ) ) {
255
+ $autoupload_interval = $c->get_integer( 'cdn.autoupload.interval' );
256
+ $schedules['w3_cdn_cron_upload'] = array(
257
+ 'interval' => $autoupload_interval,
258
+ 'display' => sprintf(
259
+ '[W3TC] CDN auto upload (every %d seconds)', $autoupload_interval
260
+ )
261
+ );
262
+ }
263
+
264
+ return $schedules;
265
+ }
266
+
267
+ /**
268
+ * Switch theme action
269
+ */
270
+ function switch_theme() {
271
+ $state = Dispatcher::config_state();
272
+ $state->set( 'cdn.show_note_theme_changed', true );
273
+ $state->save();
274
+ }
275
+
276
+ /**
277
+ * WP Upgrade action hack
278
+ *
279
+ * @param string $message
280
+ */
281
+ function update_feedback( $message ) {
282
+ if ( $message == __( 'Upgrading database' ) ) {
283
+ $state = Dispatcher::config_state();
284
+ $state->set( 'cdn.show_note_wp_upgraded', true );
285
+ $state->save();
286
+ }
287
+ }
288
+
289
+ /**
290
+ * OB Callback
291
+ *
292
+ * @param string $buffer
293
+ * @return string
294
+ */
295
+ function ob_callback( $buffer ) {
296
+ if ( $buffer != '' && Util_Content::is_html( $buffer ) ) {
297
+ if ( $this->can_cdn2( $buffer ) ) {
298
+ $srcset_helper = new _Cdn_Plugin_ContentFilter();
299
+ $buffer = $srcset_helper->replace_all_links( $buffer );
300
+ $this->_replaced_urls = $srcset_helper->get_replaced_urls();
301
+ }
302
+ }
303
+
304
+ return $buffer;
305
+ }
306
+
307
+ /**
308
+ * Returns array of files to upload
309
+ *
310
+ * @return array
311
+ */
312
+ function get_files() {
313
+ $files = array();
314
+
315
+ if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
316
+ $files = array_merge( $files, $this->get_files_includes() );
317
+ }
318
+
319
+ if ( $this->_config->get_boolean( 'cdn.theme.enable' ) ) {
320
+ $files = array_merge( $files, $this->get_files_theme() );
321
+ }
322
+
323
+ if ( $this->_config->get_boolean( 'cdn.minify.enable' ) ) {
324
+ $files = array_merge( $files, $this->get_files_minify() );
325
+ }
326
+
327
+ if ( $this->_config->get_boolean( 'cdn.custom.enable' ) ) {
328
+ $files = array_merge( $files, $this->get_files_custom() );
329
+ }
330
+
331
+ return $files;
332
+ }
333
+
334
+ /**
335
+ * Exports includes to CDN
336
+ *
337
+ * @return array
338
+ */
339
+ function get_files_includes() {
340
+ $includes_root = Util_Environment::normalize_path( ABSPATH . WPINC );
341
+ $doc_root = Util_Environment::document_root();
342
+ $includes_path = ltrim( str_replace( $doc_root, '', $includes_root ), '/' );
343
+
344
+ $files = Cdn_Util::search_files(
345
+ $includes_root, $includes_path, $this->_config->get_string( 'cdn.includes.files' )
346
+ );
347
+
348
+ return $files;
349
+ }
350
+
351
+ /**
352
+ * Exports theme to CDN
353
+ *
354
+ * @return array
355
+ */
356
+ function get_files_theme() {
357
+ /**
358
+ * If mobile or referrer support enabled
359
+ * we should upload whole themes directory
360
+ */
361
+ if ( $this->_config->get_boolean( 'mobile.enabled' )
362
+ || $this->_config->get_boolean( 'referrer.enabled' ) ) {
363
+ $themes_root = get_theme_root();
364
+ } else {
365
+ $themes_root = get_stylesheet_directory();
366
+ }
367
+
368
+ $themes_root = Util_Environment::normalize_path( $themes_root );
369
+ $themes_path = ltrim( str_replace(
370
+ Util_Environment::document_root(), '', $themes_root ), '/' );
371
+ $files = Cdn_Util::search_files(
372
+ $themes_root, $themes_path, $this->_config->get_string( 'cdn.theme.files' )
373
+ );
374
+
375
+ return $files;
376
+ }
377
+
378
+ /**
379
+ * Exports min files to CDN
380
+ *
381
+ * @return array
382
+ */
383
+ function get_files_minify() {
384
+ $files = array();
385
+
386
+ if ( $this->_config->get_boolean( 'minify.rewrite' ) &&
387
+ Util_Rule::can_check_rules() &&
388
+ ( !$this->_config->get_boolean( 'minify.auto' ) ||
389
+ Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) ) ) {
390
+
391
+
392
+ $minify = Dispatcher::component( 'Minify_Plugin' );
393
+
394
+ $document_root = Util_Environment::document_root();
395
+ $minify_root = Util_Environment::cache_blog_dir( 'minify' );
396
+ $minify_path = ltrim( str_replace( $document_root, '', $minify_root ), '/' );
397
+ $urls = $minify->get_urls();
398
+
399
+ // in WPMU + network admin (this code used for minify manual only)
400
+ // common minify files are stored under context of main blog (i.e. 1)
401
+ // but have urls of 0 blog, so download has to be used
402
+ if ( $this->_config->get_string( 'minify.engine' ) == 'file' &&
403
+ !( Util_Environment::is_wpmu() && is_network_admin() ) ) {
404
+
405
+ foreach ( $urls as $url ) {
406
+ Util_Http::get( $url );
407
+ }
408
+
409
+ $files = Cdn_Util::search_files( $minify_root,
410
+ $minify_path, '*.css;*.js' );
411
+
412
+ } else {
413
+ foreach ( $urls as $url ) {
414
+ $file = Util_Environment::normalize_file_minify( $url );
415
+ $file = Util_Environment::translate_file( $file );
416
+
417
+ if ( !Util_Environment::is_url( $file ) ) {
418
+ $file = $document_root . '/' . $file;
419
+ $file = ltrim( str_replace( $minify_root, '', $file ), '/' );
420
+
421
+ $dir = dirname( $file );
422
+
423
+ if ( $dir ) {
424
+ Util_File::mkdir( $dir, 0777, $minify_root );
425
+ }
426
+
427
+ if ( Util_Http::download( $url, $minify_root . '/' . $file ) !== false ) {
428
+ $files[] = $minify_path . '/' . $file;
429
+ }
430
+ }
431
+ }
432
+ }
433
+ }
434
+
435
+ return $files;
436
+ }
437
+
438
+ /**
439
+ * Exports custom files to CDN
440
+ *
441
+ * @return array
442
+ */
443
+ function get_files_custom() {
444
+ $files = array();
445
+ $document_root = Util_Environment::document_root();
446
+ $custom_files = $this->_config->get_array( 'cdn.custom.files' );
447
+ $custom_files = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $custom_files );
448
+ $site_root = Util_Environment::site_root();
449
+ $path = Util_Environment::site_url_uri();
450
+ $site_root_dir = str_replace( $document_root, '', $site_root );
451
+ if ( strstr( WP_CONTENT_DIR, Util_Environment::site_root() ) === false ) {
452
+ $site_root = Util_Environment::document_root();
453
+ $path = '';
454
+ }
455
+
456
+ $content_path = trim( str_replace( WP_CONTENT_DIR, '', $site_root ), '/\\' );
457
+
458
+ foreach ( $custom_files as $custom_file ) {
459
+ if ( $custom_file != '' ) {
460
+ $custom_file = Cdn_Util::replace_folder_placeholders( $custom_file );
461
+ $custom_file = Util_Environment::normalize_file( $custom_file );
462
+
463
+ if ( !Util_Environment::is_wpmu() ) {
464
+ $dir = trim( dirname( $custom_file ), '/\\' );
465
+ $rel_path = trim( dirname( $custom_file ), '/\\' );
466
+ } else
467
+ $rel_path = $dir = trim( dirname( $custom_file ), '/\\' );
468
+
469
+ if ( strpos( $dir, '<currentblog>' ) != false ) {
470
+ $rel_path = $dir = str_replace(
471
+ '<currentblog>', 'blogs.dir/'
472
+ . Util_Environment::blog_id(), $dir
473
+ );
474
+ }
475
+
476
+ if ( $dir == '.' ) {
477
+ $rel_path = $dir = '';
478
+ }
479
+ $mask = basename( $custom_file );
480
+ $files = array_merge(
481
+ $files, Cdn_Util::search_files( $document_root . '/'
482
+ . $dir, $rel_path, $mask )
483
+ );
484
+ }
485
+ }
486
+
487
+ return $files;
488
+ }
489
+
490
+ /**
491
+ * Check if we can do CDN logic
492
+ *
493
+ * @return boolean
494
+ */
495
+ function can_cdn() {
496
+ /**
497
+ * Skip if admin
498
+ */
499
+ if ( defined( 'WP_ADMIN' ) ) {
500
+ $this->cdn_reject_reason = 'wp-admin';
501
+
502
+ return false;
503
+ }
504
+
505
+ /**
506
+ * Check for WPMU's and WP's 3.0 short init
507
+ */
508
+ if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
509
+ $this->cdn_reject_reason = 'Short init';
510
+
511
+ return false;
512
+ }
513
+
514
+ /**
515
+ * Check User agent
516
+ */
517
+ if ( !$this->check_ua() ) {
518
+ $this->cdn_reject_reason = 'user agent is rejected';
519
+
520
+ return false;
521
+ }
522
+
523
+ /**
524
+ * Check request URI
525
+ */
526
+ if ( !$this->_check_request_uri() ) {
527
+ $this->cdn_reject_reason = 'request URI is rejected';
528
+
529
+ return false;
530
+ }
531
+
532
+ /**
533
+ * Do not replace urls if SSL and SSL support is do not replace
534
+ */
535
+ if ( Util_Environment::is_https() && $this->_config->get_boolean( 'cdn.reject.ssl' ) ) {
536
+ $this->cdn_reject_reason = 'SSL is rejected';
537
+
538
+ return false;
539
+ }
540
+
541
+ return true;
542
+ }
543
+
544
+ /**
545
+ * Returns true if we can do CDN logic
546
+ *
547
+ * @param unknown $buffer
548
+ * @return string
549
+ */
550
+ function can_cdn2( $buffer ) {
551
+ /**
552
+ * Check for database error
553
+ */
554
+ if ( Util_Content::is_database_error( $buffer ) ) {
555
+ $this->cdn_reject_reason = 'Database Error occurred';
556
+
557
+ return false;
558
+ }
559
+
560
+ /**
561
+ * Check for DONOTCDN constant
562
+ */
563
+ if ( defined( 'DONOTCDN' ) && DONOTCDN ) {
564
+ $this->cdn_reject_reason = 'DONOTCDN constant is defined';
565
+
566
+ return false;
567
+ }
568
+
569
+ /**
570
+ * Check logged users roles
571
+ */
572
+ if ( $this->_config->get_boolean(
573
+ 'cdn.reject.logged_roles' ) && !$this->_check_logged_in_role_allowed()
574
+ ) {
575
+ $this->cdn_reject_reason = 'logged in role is rejected';
576
+
577
+ return false;
578
+ }
579
+
580
+ return true;
581
+ }
582
+
583
+ /**
584
+ * Checks User Agent
585
+ *
586
+ * @return boolean
587
+ */
588
+ function check_ua() {
589
+ $uas = array_merge( $this->_config->get_array( 'cdn.reject.ua' ), array(
590
+ W3TC_POWERED_BY
591
+ ) );
592
+
593
+ foreach ( $uas as $ua ) {
594
+ if ( !empty( $ua ) ) {
595
+ if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && stristr(
596
+ $_SERVER['HTTP_USER_AGENT'], $ua ) !== false
597
+ )
598
+ return false;
599
+ }
600
+ }
601
+
602
+ return true;
603
+ }
604
+
605
+ /**
606
+ * Checks request URI
607
+ *
608
+ * @return boolean
609
+ */
610
+ function _check_request_uri() {
611
+ $reject_uri = $this->_config->get_array( 'cdn.reject.uri' );
612
+ $reject_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $reject_uri );
613
+
614
+ foreach ( $reject_uri as $expr ) {
615
+ $expr = trim( $expr );
616
+ if ( $expr != '' && preg_match( '~' . $expr . '~i', $_SERVER['REQUEST_URI'] ) ) {
617
+ return false;
618
+ }
619
+ }
620
+
621
+
622
+ if ( Util_Request::get_string( 'wp_customize' ) )
623
+ return false;
624
+
625
+ return true;
626
+ }
627
+ /**
628
+ * Check if logged in user role is allwed to use CDN
629
+ *
630
+ * @return boolean
631
+ */
632
+ private function _check_logged_in_role_allowed() {
633
+ global $current_user;
634
+
635
+ if ( !is_user_logged_in() )
636
+ return true;
637
+
638
+ $roles = $this->_config->get_array( 'cdn.reject.roles' );
639
+
640
+ if ( empty( $roles ) || empty( $current_user->roles ) ||
641
+ !is_array( $current_user->roles ) )
642
+ return true;
643
+
644
+ foreach ( $current_user->roles as $role ) {
645
+ if ( in_array( $role, $roles ) )
646
+ return false;
647
+ }
648
+
649
+ return true;
650
+ }
651
+
652
+ /**
653
+ * media_row_actions filter
654
+ *
655
+ * @param array $actions
656
+ * @param object $post
657
+ * @return array
658
+ */
659
+ function media_row_actions( $actions, $post ) {
660
+ return $this->get_admin()->media_row_actions( $actions, $post );
661
+ }
662
+
663
+
664
+ /**
665
+ *
666
+ *
667
+ * @param unknown $current_state
668
+ * @return bool
669
+ */
670
+ function cdn_is_running( $current_state ) {
671
+ $admin = $this->get_admin();
672
+ return $admin->is_running();
673
+ }
674
+
675
+ /**
676
+ * Change canonical header
677
+ */
678
+ function change_canonical_header() {
679
+ $admin = $this->get_admin();
680
+ $admin->change_canonical_header();
681
+ }
682
+
683
+ public function w3tc_admin_bar_menu( $menu_items ) {
684
+ $cdn_engine = $this->_config->get_string( 'cdn.engine' );
685
+
686
+ if ( Cdn_Util::can_purge_all( $cdn_engine ) ) {
687
+ $menu_items['20710.cdn'] = array(
688
+ 'id' => 'w3tc_cdn_flush_all',
689
+ 'parent' => 'w3tc_flush',
690
+ 'title' => __( 'CDN: All', 'w3-total-cache' ),
691
+ 'href' => wp_nonce_url( network_admin_url(
692
+ 'admin.php?page=w3tc_cdn&amp;w3tc_flush_cdn' ),
693
+ 'w3tc' )
694
+ );
695
+ }
696
+
697
+ if ( Cdn_Util::can_purge( $cdn_engine ) ) {
698
+ $menu_items['20790.cdn'] = array(
699
+ 'id' => 'w3tc_cdn_flush',
700
+ 'parent' => 'w3tc_flush',
701
+ 'title' => __( 'CDN: Manual Purge', 'w3-total-cache' ),
702
+ 'href' => wp_nonce_url( network_admin_url( 'admin.php?page=w3tc_cdn&amp;w3tc_cdn_purge' ), 'w3tc' ),
703
+ 'meta' => array( 'onclick' => "w3tc_popupadmin_bar(this.href); return false" )
704
+ );
705
+ }
706
+
707
+ return $menu_items;
708
+ }
709
+
710
+ public function w3tc_footer_comment( $strings ) {
711
+ $common = Dispatcher::component( 'Cdn_Core' );
712
+ $cdn = $common->get_cdn();
713
+ $via = $cdn->get_via();
714
+
715
+ $strings[] = sprintf(
716
+ __( 'Content Delivery Network via %s%s', 'w3-total-cache' ),
717
+ ( $via ? $via : 'N/A' ),
718
+ ( empty( $this->cdn_reject_reason ) ? '' :
719
+ sprintf( ' (%s)', $this->cdn_reject_reason ) ) );
720
+
721
+ if ( $this->_config->get_boolean( 'cdn.debug' ) ) {
722
+ $strings[] = "CDN debug info:";
723
+ $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ),
724
+ $this->_config->get_string( 'cdn.engine' ) );
725
+
726
+ if ( $this->cdn_reject_reason ) {
727
+ $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ),
728
+ $this->cdn_reject_reason );
729
+ }
730
+
731
+ if ( count( $this->_replaced_urls ) ) {
732
+ $strings[] = "Replaced URLs:";
733
+
734
+ foreach ( $this->_footer_comment_postfix as $old_url => $new_url ) {
735
+ $strings[] = sprintf( "%s => %s",
736
+ Util_Content::escape_comment( $old_url ),
737
+ Util_Content::escape_comment( $new_url ) );
738
+ }
739
+ }
740
+ }
741
+
742
+ return $strings;
743
+ }
744
+ }
745
+
746
+ class _Cdn_Plugin_ContentFilter {
747
+
748
+ private $_regexps = array();
749
+ private $_placeholders = array();
750
+ private $_config;
751
+ private $_replaced_urls;
752
+ /**
753
+ * If background uploading already scheduled
754
+ *
755
+ * @var boolean
756
+ */
757
+ private static $_upload_scheduled = false;
758
+
759
+ function __construct() {
760
+ $this->_config = Dispatcher::config();
761
+ }
762
+
763
+ function replace_all_links( $buffer ) {
764
+ $this->fill_regexps();
765
+
766
+ $srcset_pattern = '~srcset\s*=\s*[\"\'](.*?)[\"\']~';
767
+ $buffer = preg_replace_callback(
768
+ $srcset_pattern, array( $this, '_srcset_replace_callback' ), $buffer
769
+ );
770
+
771
+ foreach ( $this->_regexps as $regexp ) {
772
+ $buffer = preg_replace_callback(
773
+ $regexp, array( $this, '_link_replace_callback' ), $buffer
774
+ );
775
+ }
776
+
777
+ if ( $this->_config->get_boolean( 'cdn.minify.enable' ) ) {
778
+ if ( $this->_config->get_boolean( 'minify.auto' ) ) {
779
+ $regexp = '~(["\'(=])\s*' .
780
+ $this->minify_url_regexp( '/[a-zA-Z0-9-_]+\.(css|js)' ) .
781
+ '~U';
782
+ if ( Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) )
783
+ $processor = array( $this, '_link_replace_callback' );
784
+ else
785
+ $processor = array( $this, '_minify_auto_pushcdn_link_replace_callback' );
786
+ } else {
787
+ $regexp = '~(["\'(=])\s*' .
788
+ $this->minify_url_regexp(
789
+ '/[a-z0-9]+\..+\.include(-(footer|body))?(-nb)?\.[a-f0-9]+\.(css|js)' )
790
+ .'~U';
791
+ $processor = array( $this, '_link_replace_callback' );
792
+ }
793
+
794
+ $buffer = preg_replace_callback( $regexp, $processor, $buffer );
795
+ }
796
+
797
+ $buffer = $this->replace_placeholders( $buffer );
798
+
799
+ return $buffer;
800
+ }
801
+
802
+ /**
803
+ * Link replace callback
804
+ *
805
+ * @param array $matches
806
+ * @return string
807
+ */
808
+ function _link_replace_callback( $matches ) {
809
+ list( $match, $quote, $url, , , , $path ) = $matches;
810
+ $path = ltrim( $path, '/' );
811
+ $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
812
+ if ( is_null( $r ) ) {
813
+ $r = $this->_link_replace_callback_ask_cdn( $match, $quote, $url, $path );
814
+ }
815
+
816
+ return $r;
817
+ }
818
+
819
+ function _srcset_replace_callback( $matches ) {
820
+ list( $match, $srcset ) = $matches;
821
+ if ( empty( $this->_regexps ) )
822
+ return $match;
823
+ $index = "%srcset-" . count( $this->_placeholders ) . "%";
824
+
825
+ $srcset_urls = explode( ',', $srcset );
826
+ $new_srcset_urls = array();
827
+
828
+ foreach ( $srcset_urls as $set ) {
829
+
830
+ preg_match( "~(?P<spaces>^\s*)(?P<url>\S+)(?P<rest>.*)~", $set, $parts );
831
+ if ( isset( $parts['url'] ) ) {
832
+
833
+ foreach ( $this->_regexps as $regexp ) {
834
+ $new_url = preg_replace_callback( $regexp, array(
835
+ $this,
836
+ '_link_replace_callback'
837
+ ), '"' . $parts['url'] . '">' );
838
+
839
+ if ( '"' . $parts['url'] . '">' != $new_url ) {
840
+ $parts['url'] = substr( $new_url, 1, -2 );
841
+ break;
842
+ }
843
+ }
844
+ $new_srcset_urls[] = $parts['spaces'] .$parts['url']
845
+ . $parts['rest'];
846
+ } else {
847
+ $new_srcset_urls[] = $set;
848
+ }
849
+
850
+ }
851
+ $this->_placeholders[$index] = implode( ',', $new_srcset_urls );
852
+ return 'srcset="' . $index . '"';
853
+ }
854
+
855
+ private function replace_placeholders( $buffer ) {
856
+ foreach ( $this->_placeholders as $srcset_id => $srcset_content ) {
857
+ $buffer = str_replace( $srcset_id, $srcset_content, $buffer );
858
+ }
859
+ return $buffer;
860
+ }
861
+
862
+ /**
863
+ * Gets regexp for minified files
864
+ *
865
+ * @return string
866
+ */
867
+ private function minify_url_regexp( $filename_mask ) {
868
+ $minify_base_url = Util_Environment::filename_to_url(
869
+ Util_Environment::cache_blog_minify_dir()
870
+ );
871
+ $matches = null;
872
+ if ( !preg_match( '~((https?://)?([^/]+))(.+)~i', $minify_base_url, $matches ) )
873
+ return '';
874
+
875
+ $protocol_domain_regexp = Util_Environment::get_url_regexp( $matches[1] );
876
+ $path_regexp = Util_Environment::preg_quote( $matches[4] );
877
+
878
+ $regexp =
879
+ '(' .
880
+ '(' . $protocol_domain_regexp . ')?' .
881
+ '(' . $path_regexp . $filename_mask . ')' .
882
+ ')';
883
+ return $regexp;
884
+ }
885
+
886
+ /**
887
+ *
888
+ *
889
+ * @param unknown $domain_url_regexp
890
+ * @param unknown $baseurl
891
+ * @param unknown $upload_info
892
+ * @param unknown $regexps
893
+ * @return array
894
+ */
895
+ private function make_uploads_regexes( $domain_url_regexp, $baseurl,
896
+ $upload_info, $regexps ) {
897
+ if ( preg_match( '~' . $domain_url_regexp . '~i', $baseurl ) ) {
898
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?('
899
+ . Util_Environment::preg_quote( $upload_info['baseurlpath'] )
900
+ . '([^"\')>]+)))~i';
901
+ } else {
902
+ $parsed = @parse_url( $baseurl );
903
+ $upload_url_domain_regexp = isset( $parsed['host'] )
904
+ ? Util_Environment::get_url_regexp( $parsed['scheme'] . '://'
905
+ . $parsed['host'] ) : $domain_url_regexp;
906
+ $baseurlpath = isset( $parsed['path'] ) ? rtrim( $parsed['path'], '/' ) : '';
907
+ if ( $baseurlpath )
908
+ $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp . ')?('
909
+ . Util_Environment::preg_quote( $baseurlpath )
910
+ . '([^"\'>]+)))~i';
911
+ else
912
+ $regexps[] = '~(["\'])\s*((' . $upload_url_domain_regexp
913
+ . ')(([^"\'>]+)))~i';
914
+ }
915
+ return $regexps;
916
+ }
917
+
918
+ private function fill_regexps() {
919
+ $regexps = array();
920
+
921
+ $site_path = Util_Environment::site_url_uri();
922
+ $domain_url_regexp = Util_Environment::home_domain_root_url_regexp();
923
+
924
+ $site_domain_url_regexp = false;
925
+ if ( $domain_url_regexp != Util_Environment::get_url_regexp(
926
+ Util_Environment::url_to_host( site_url() ) ) )
927
+ $site_domain_url_regexp = Util_Environment::get_url_regexp(
928
+ Util_Environment::url_to_host( site_url() )
929
+ );
930
+
931
+ if ( $this->_config->get_boolean( 'cdn.uploads.enable' ) ) {
932
+ $upload_info = Util_Http::upload_info();
933
+
934
+ if ( $upload_info ) {
935
+ $baseurl = $upload_info['baseurl'];
936
+
937
+ if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
938
+ $parsed = @parse_url( $upload_info['baseurl'] );
939
+ $baseurl = home_url() . $parsed['path'];
940
+ }
941
+
942
+ $regexps = $this->make_uploads_regexes(
943
+ $domain_url_regexp, $baseurl, $upload_info, $regexps
944
+ );
945
+ if ( $site_domain_url_regexp )
946
+ $regexps = $this->make_uploads_regexes(
947
+ $site_domain_url_regexp, $baseurl, $upload_info, $regexps
948
+ );
949
+ }
950
+ }
951
+
952
+ if ( $this->_config->get_boolean( 'cdn.includes.enable' ) ) {
953
+ $mask = $this->_config->get_string( 'cdn.includes.files' );
954
+ if ( $mask != '' ) {
955
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp .
956
+ ')?(' .
957
+ Util_Environment::preg_quote( $site_path . WPINC ) .
958
+ '/(' . Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
959
+ if ( $site_domain_url_regexp )
960
+ $regexps[] = '~(["\'(=])\s*((' .
961
+ $site_domain_url_regexp . ')?(' .
962
+ Util_Environment::preg_quote( $site_path . WPINC ) .
963
+ '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
964
+ ')([^"\'() >]*)))~i';
965
+ }
966
+ }
967
+
968
+ if ( $this->_config->get_boolean( 'cdn.theme.enable' ) ) {
969
+ $theme_dir = preg_replace( '~'
970
+ . $domain_url_regexp . '~i', '', get_theme_root_uri() );
971
+
972
+ $mask = $this->_config->get_string( 'cdn.theme.files' );
973
+
974
+ if ( $mask != '' ) {
975
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp . ')?(' .
976
+ Util_Environment::preg_quote( $theme_dir ) . '/(' .
977
+ Cdn_Util::get_regexp_by_mask( $mask ) . ')([^"\'() >]*)))~i';
978
+ if ( $site_domain_url_regexp ) {
979
+ $theme_dir2 = preg_replace( '~' . $site_domain_url_regexp
980
+ . '~i', '', get_theme_root_uri() );
981
+ $regexps[] = '~(["\'(=])\s*((' .
982
+ $site_domain_url_regexp . ')?(' .
983
+ Util_Environment::preg_quote( $theme_dir ) . '/(' .
984
+ Cdn_Util::get_regexp_by_mask( $mask ) .
985
+ ')([^"\'() >]*)))~i';
986
+ $regexps[] = '~(["\'(=])\s*((' .
987
+ $site_domain_url_regexp . ')?(' .
988
+ Util_Environment::preg_quote( $theme_dir2 ) .
989
+ '/(' . Cdn_Util::get_regexp_by_mask( $mask ) .
990
+ ')([^"\'() >]*)))~i';
991
+ }
992
+ }
993
+ }
994
+
995
+ if ( $this->_config->get_boolean( 'cdn.custom.enable' ) ) {
996
+ $masks = $this->_config->get_array( 'cdn.custom.files' );
997
+ $masks = array_map( array( '\W3TC\Cdn_Util', 'replace_folder_placeholders' ), $masks );
998
+ $masks = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $masks );
999
+
1000
+ if ( count( $masks ) ) {
1001
+ $mask_regexps = array();
1002
+
1003
+ foreach ( $masks as $mask ) {
1004
+ if ( $mask != '' ) {
1005
+ $mask = Util_Environment::normalize_file( $mask );
1006
+ $mask_regexps[] = Cdn_Util::get_regexp_by_mask( $mask );
1007
+ }
1008
+ }
1009
+
1010
+ $regexps[] = '~(["\'(=])\s*((' . $domain_url_regexp .
1011
+ ')?(' . Util_Environment::preg_quote( $site_path ) .
1012
+ '(' . implode( '|', $mask_regexps ) . ')([^"\'() >]*)))~i';
1013
+ if ( $site_domain_url_regexp )
1014
+ $regexps[] = '~(["\'(=])\s*((' .
1015
+ $site_domain_url_regexp . ')?(' .
1016
+ Util_Environment::preg_quote( $site_path ) . '(' .
1017
+ implode( '|', $mask_regexps ) . ')([^"\'() >]*)))~i';
1018
+ }
1019
+ }
1020
+
1021
+ $this->_regexps = $regexps;
1022
+ }
1023
+
1024
+ /**
1025
+ * Link replace callback, basic checks step
1026
+ *
1027
+ * @param string $match
1028
+ * @param string $quote
1029
+ * @param string $url
1030
+ * @param string $path
1031
+ * @return null|string
1032
+ */
1033
+ function _link_replace_callback_checks( $match, $quote, $url, $path ) {
1034
+ global $wpdb;
1035
+ static $queue = null, $reject_files = null;
1036
+
1037
+ /**
1038
+ * Check if URL was already replaced
1039
+ */
1040
+ if ( isset( $this->replaced_urls[$url] ) ) {
1041
+ return $quote . $this->replaced_urls[$url];
1042
+ }
1043
+
1044
+ /**
1045
+ * Check URL for rejected files
1046
+ */
1047
+ if ( $reject_files === null ) {
1048
+ $reject_files = $this->_config->get_array( 'cdn.reject.files' );
1049
+ }
1050
+
1051
+ foreach ( $reject_files as $reject_file ) {
1052
+ if ( $reject_file != '' ) {
1053
+ $reject_file = Cdn_Util::replace_folder_placeholders( $reject_file );
1054
+
1055
+ $reject_file = Util_Environment::normalize_file( $reject_file );
1056
+
1057
+ $reject_file_regexp = '~^('
1058
+ . Cdn_Util::get_regexp_by_mask( $reject_file ) . ')~i';
1059
+
1060
+ if ( preg_match( $reject_file_regexp, $path ) ) {
1061
+ return $match;
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ /**
1067
+ * Don't replace URL for files that are in the CDN queue
1068
+ */
1069
+ if ( $queue === null ) {
1070
+ if ( !Cdn_Util::is_engine_mirror( $this->_config->get_string( 'cdn.engine' ) ) ) {
1071
+ $sql = $wpdb->prepare( 'SELECT remote_path FROM '
1072
+ . $wpdb->base_prefix . W3TC_CDN_TABLE_QUEUE
1073
+ . ' WHERE remote_path = %s', $path );
1074
+ $queue = $wpdb->get_var( $sql );
1075
+ }
1076
+ else {
1077
+ $queue = false;
1078
+ }
1079
+ }
1080
+ if ( $queue ) {
1081
+ return $match;
1082
+ }
1083
+ return null;
1084
+ }
1085
+
1086
+ /**
1087
+ * Link replace callback, url replacement using cdn engine
1088
+ *
1089
+ * @param string $match
1090
+ * @param string $quote
1091
+ * @param string $url
1092
+ * @param string $path
1093
+ * @return null|string
1094
+ */
1095
+ function _link_replace_callback_ask_cdn( $match, $quote, $url, $path ) {
1096
+ $common = Dispatcher::component( 'Cdn_Core' );
1097
+ $cdn = $common->get_cdn();
1098
+ $remote_path = $common->uri_to_cdn_uri( $path );
1099
+ $new_url = $cdn->format_url( $remote_path );
1100
+ if ( $new_url ) {
1101
+ $is_engine_mirror = Cdn_Util::is_engine_mirror(
1102
+ $this->_config->get_string( 'cdn.engine' ) );
1103
+
1104
+ $new_url = apply_filters( 'w3tc_cdn_url', $new_url, $url,
1105
+ $is_engine_mirror );
1106
+ $this->replaced_urls[$url] = $new_url;
1107
+ return $quote . $new_url;
1108
+ }
1109
+
1110
+ return $match;
1111
+ }
1112
+
1113
+ /**
1114
+ * Link replace callback for urls from minify module using auto mode and in cdn of push type
1115
+ *
1116
+ * @param array $matches
1117
+ * @return string
1118
+ */
1119
+ function _minify_auto_pushcdn_link_replace_callback( $matches ) {
1120
+ static $dispatcher = null;
1121
+
1122
+ list( $match, $quote, $url, , , , $path ) = $matches;
1123
+ $path = ltrim( $path, '/' );
1124
+ $r = $this->_link_replace_callback_checks( $match, $quote, $url, $path );
1125
+
1126
+ /**
1127
+ * Check if we can replace that URL (for auto mode it should be uploaded)
1128
+ */
1129
+ if ( !Dispatcher::is_url_cdn_uploaded( $url ) ) {
1130
+ Dispatcher::component( 'Cdn_Core' )->queue_upload_url( $url );
1131
+ if ( !self::$_upload_scheduled ) {
1132
+ wp_schedule_single_event( time(), 'w3_cdn_cron_queue_process' );
1133
+ add_action( 'shutdown', 'wp_cron' );
1134
+
1135
+ self::$_upload_scheduled = true;
1136
+ }
1137
+
1138
+
1139
+ return $match;
1140
+ }
1141
+
1142
+ if ( is_null( $r ) ) {
1143
+ $r = $this->_link_replace_callback_ask_cdn( $match, $quote, $url, $path );
1144
+ }
1145
+ return $r;
1146
+ }
1147
+
1148
+ function get_replaced_urls() {
1149
+ $strings = array();
1150
+ if ( count( $this->_replaced_urls ) ) {
1151
+ $strings[] = "Replaced URLs:";
1152
+
1153
+ foreach ( $this->_replaced_urls as $old_url => $new_url ) {
1154
+ $strings[] = sprintf( "%s => %s",
1155
+ Util_Content::escape_comment( $old_url ),
1156
+ Util_Content::escape_comment( $new_url ) );
1157
+ }
1158
+ }
1159
+ return $strings;
1160
+ }
1161
+
1162
+ }
Cdn_Plugin_Admin.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_Plugin_Admin {
5
+ function run() {
6
+ $config_labels = new Cdn_ConfigLabels();
7
+ add_filter( 'w3tc_config_labels', array( $config_labels, 'config_labels' ) );
8
+
9
+ $c = Dispatcher::config();
10
+ $cdn_engine = $c->get_string( 'cdn.engine' );
11
+
12
+ if ( $c->get_boolean( 'cdn.enabled' ) &&
13
+ !Cdn_Util::is_engine_fsd( $cdn_engine ) ) {
14
+ $admin_notes = new Cdn_AdminNotes();
15
+ add_filter( 'w3tc_notes', array( $admin_notes, 'w3tc_notes' ) );
16
+ add_filter( 'w3tc_errors', array( $admin_notes, 'w3tc_errors' ) );
17
+ }
18
+
19
+
20
+ // attach to actions without firing class loading at all without need
21
+ if ( $cdn_engine == 'cloudfront_fsd' ) {
22
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
23
+ '\W3TC\Cdn_CloudFrontFsd_Page',
24
+ 'admin_print_scripts_w3tc_cdn' ) );
25
+ add_action( 'w3tc_ajax', array(
26
+ '\W3TC\Cdn_CloudFrontFsd_Popup',
27
+ 'w3tc_ajax' ) );
28
+ add_action( 'w3tc_settings_cdn', array(
29
+ '\W3TC\Cdn_CloudFrontFsd_Page',
30
+ 'w3tc_settings_cdn' ) );
31
+ } elseif ( $cdn_engine == 'google_drive' ) {
32
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
33
+ '\W3TC\Cdn_GoogleDrive_Page',
34
+ 'admin_print_scripts_w3tc_cdn' ) );
35
+ add_action( 'w3tc_settings_cdn_boxarea_configuration', array(
36
+ '\W3TC\Cdn_GoogleDrive_Page',
37
+ 'w3tc_settings_cdn_boxarea_configuration'
38
+ ) );
39
+ } elseif ( $cdn_engine == 'highwinds' ) {
40
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
41
+ '\W3TC\Cdn_Highwinds_Page',
42
+ 'admin_print_scripts_w3tc_cdn' ) );
43
+ add_action( 'w3tc_ajax', array(
44
+ '\W3TC\Cdn_Highwinds_Popup',
45
+ 'w3tc_ajax' ) );
46
+ add_action( 'admin_init_w3tc_dashboard', array(
47
+ '\W3TC\Cdn_Highwinds_Widget',
48
+ 'admin_init_w3tc_dashboard' ) );
49
+ add_action( 'w3tc_ajax_cdn_highwinds_widgetdata', array(
50
+ '\W3TC\Cdn_Highwinds_Widget',
51
+ 'w3tc_ajax_cdn_highwinds_widgetdata' ) );
52
+ add_action( 'w3tc_settings_cdn_boxarea_configuration', array(
53
+ '\W3TC\Cdn_Highwinds_Page',
54
+ 'w3tc_settings_cdn_boxarea_configuration' ) );
55
+ } elseif ( $cdn_engine == 'maxcdn_fsd' ) {
56
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
57
+ '\W3TC\Cdn_MaxCdnFsd_Page',
58
+ 'admin_print_scripts_w3tc_cdn' ) );
59
+ add_action( 'w3tc_ajax', array(
60
+ '\W3TC\Cdn_MaxCdnFsd_Popup',
61
+ 'w3tc_ajax' ) );
62
+ add_action( 'w3tc_settings_cdn', array(
63
+ '\W3TC\Cdn_MaxCdnFsd_Page',
64
+ 'w3tc_settings_cdn' ) );
65
+ } elseif ( $cdn_engine == 'rackspace_cdn' ) {
66
+ add_filter( 'w3tc_admin_actions', array(
67
+ '\W3TC\Cdn_RackSpaceCdn_Page',
68
+ 'w3tc_admin_actions' ) );
69
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
70
+ '\W3TC\Cdn_RackSpaceCdn_Page',
71
+ 'admin_print_scripts_w3tc_cdn' ) );
72
+ add_action( 'w3tc_ajax', array(
73
+ '\W3TC\Cdn_RackSpaceCdn_Popup',
74
+ 'w3tc_ajax' ) );
75
+ add_action( 'w3tc_settings_cdn_boxarea_configuration', array(
76
+ '\W3TC\Cdn_RackSpaceCdn_Page',
77
+ 'w3tc_settings_cdn_boxarea_configuration' ) );
78
+ } elseif ( $cdn_engine == 'rscf' ) {
79
+ add_action( 'admin_print_scripts-performance_page_w3tc_cdn', array(
80
+ '\W3TC\Cdn_RackSpaceCloudFiles_Page',
81
+ 'admin_print_scripts_w3tc_cdn' ) );
82
+ add_action( 'w3tc_ajax', array(
83
+ '\W3TC\Cdn_RackSpaceCloudFiles_Popup',
84
+ 'w3tc_ajax' ) );
85
+ add_action( 'w3tc_settings_cdn_boxarea_configuration', array(
86
+ '\W3TC\Cdn_RackSpaceCloudFiles_Page',
87
+ 'w3tc_settings_cdn_boxarea_configuration' ) );
88
+ }
89
+
90
+ add_action( 'w3tc_settings_general_boxarea_cdn', array(
91
+ $this,
92
+ 'w3tc_settings_general_boxarea_cdn'
93
+ ) );
94
+ }
95
+
96
+
97
+
98
+ public function w3tc_settings_general_boxarea_cdn() {
99
+ $config = Dispatcher::config();
100
+
101
+ $engine_optgroups = array();
102
+ $engine_values = array();
103
+
104
+ $is_fsd = Util_Environment::is_w3tc_pro( $config );
105
+
106
+ if ( $is_fsd ) {
107
+ $engine_optgroups[] = __( 'Full Site Delivery:', 'w3-total-cache' );
108
+ $engine_values['cloudfront_fsd'] = array(
109
+ 'label' => __( 'Amazon CloudFront', 'w3-total-cache' ),
110
+ 'optgroup' => 0
111
+ );
112
+ $engine_values['maxcdn_fsd'] = array(
113
+ 'label' => __( 'MaxCDN (recommended)', 'w3-total-cache' ),
114
+ 'optgroup' => 0
115
+ );
116
+
117
+ $optgroup_pull = count( $engine_optgroups );
118
+ $engine_optgroups[] = __( 'Origin Pull / Mirror:', 'w3-total-cache' );
119
+ } else {
120
+ $optgroup_pull = count( $engine_optgroups );
121
+ $engine_optgroups[] = __( 'Origin Pull / Mirror:', 'w3-total-cache' );
122
+ }
123
+
124
+ $optgroup_push = count( $engine_optgroups );
125
+ $engine_optgroups[] = __( 'Origin Push:', 'w3-total-cache' );
126
+
127
+
128
+ $engine_values['akamai'] = array(
129
+ 'label' => __( 'Akamai', 'w3-total-cache' ),
130
+ 'optgroup' => $optgroup_pull
131
+ );
132
+ $engine_values['cf2'] = array(
133
+ 'label' => __( 'Amazon CloudFront', 'w3-total-cache' ),
134
+ 'disabled' => ( !Util_Installed::curl() ? true : null ),
135
+ 'optgroup' => $optgroup_pull
136
+ );
137
+ $engine_values['att'] = array(
138
+ 'label' => __( 'AT&amp;T', 'w3-total-cache' ),
139
+ 'optgroup' => $optgroup_pull
140
+ );
141
+ $engine_values['cotendo'] = array(
142
+ 'label' => __( 'Cotendo (Akamai)', 'w3-total-cache' ),
143
+ 'optgroup' => $optgroup_pull
144
+ );
145
+ $engine_values['mirror'] = array(
146
+ 'label' => __( 'Generic Mirror', 'w3-total-cache' ),
147
+ 'optgroup' => $optgroup_pull
148
+ );
149
+ $engine_values['highwinds'] = array(
150
+ 'label' => __( 'Highwinds', 'w3-total-cache' ),
151
+ 'optgroup' => $optgroup_pull
152
+ );
153
+ $engine_values['maxcdn'] = array(
154
+ 'label' => __( 'MaxCDN', 'w3-total-cache' ),
155
+ 'optgroup' => $optgroup_pull
156
+ );
157
+ $engine_values['netdna'] = array(
158
+ 'label' => __( 'MaxCDN Enterprise (NetDNA)', 'w3-total-cache' ),
159
+ 'optgroup' => $optgroup_pull
160
+ );
161
+ $engine_values['rackspace_cdn'] = array(
162
+ 'label' => __( 'RackSpace CDN', 'w3-total-cache' ),
163
+ 'optgroup' => $optgroup_pull
164
+ );
165
+ $engine_values['edgecast'] = array(
166
+ 'label' => __( 'Verizon Digital Media Services (EdgeCast) / Media Temple ProCDN', 'w3-total-cache' ),
167
+ 'optgroup' => $optgroup_pull
168
+ );
169
+ $engine_values['cf'] = array(
170
+ 'disabled' => ( !Util_Installed::curl() ? true : null ),
171
+ 'label' => __( 'Amazon CloudFront', 'w3-total-cache' ),
172
+ 'optgroup' => $optgroup_push
173
+ );
174
+ $engine_values['s3'] = array(
175
+ 'disabled' => ( !Util_Installed::curl() ? true : null ),
176
+ 'label' => __( 'Amazon Simple Storage Service (S3)', 'w3-total-cache' ),
177
+ 'optgroup' => $optgroup_push
178
+ );
179
+ $engine_values['s3_compatible'] = array(
180
+ 'disabled' => ( !Util_Installed::curl() ? true : null ),
181
+ 'label' => __( 'Amazon Simple Storage Service (S3) Compatible', 'w3-total-cache' ),
182
+ 'optgroup' => $optgroup_push
183
+ );
184
+ $engine_values['google_drive'] = array(
185
+ 'label' => __( 'Google Drive', 'w3-total-cache' ),
186
+ 'optgroup' => $optgroup_push
187
+ );
188
+ $engine_values['azure'] = array(
189
+ 'label' => __( 'Microsoft Azure Storage', 'w3-total-cache' ),
190
+ 'optgroup' => $optgroup_push
191
+ );
192
+ $engine_values['rscf'] = array(
193
+ 'disabled' => ( !Util_Installed::curl() ? true : null ),
194
+ 'label' => __( 'Rackspace Cloud Files', 'w3-total-cache' ),
195
+ 'optgroup' => $optgroup_push
196
+ );
197
+ $engine_values['ftp'] = array(
198
+ 'disabled' => ( !Util_Installed::ftp() ? true : null ),
199
+ 'label' => __( 'Self-hosted / File Transfer Protocol Upload', 'w3-total-cache' ),
200
+ 'optgroup' => $optgroup_push
201
+ );
202
+
203
+ $cdn_enabled = $config->get_boolean( 'cdn.enabled' );
204
+
205
+ $cdn_engine = $config->get_string( 'cdn.engine' );
206
+
207
+ $tag = '';
208
+ if ( $cdn_engine == 'cloudfront_fsd' )
209
+ $tag = '#cdn-fsd-cloudfront';
210
+ elseif ( $cdn_engine == 'maxcdn_fsd' )
211
+ $tag = '#cdn-fsd-maxcdn';
212
+
213
+ if ( empty( $tag ) )
214
+ $cdn_engine_extra_description = '';
215
+ else
216
+ $cdn_engine_extra_description =
217
+ ' See <a href="admin.php?page=w3tc_faq' . $tag .
218
+ '">setup instructions</a>';
219
+
220
+ include W3TC_DIR . '/Cdn_GeneralPage_View.php';
221
+ }
222
+ }
Cdn_Plugin_WidgetMaxCdn.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * MaxCDN Widget
6
+ */
7
+ class Cdn_Plugin_WidgetMaxCdn {
8
+ private $authorized;
9
+ private $have_zone;
10
+ private $_sealed;
11
+
12
+ /**
13
+ *
14
+ *
15
+ * @var NetDNA
16
+ */
17
+ private $api;
18
+
19
+ /**
20
+ * Config
21
+ */
22
+ private $_config = null;
23
+
24
+ function __construct() {
25
+ $this->_config = Dispatcher::config();
26
+ }
27
+
28
+ /**
29
+ * Runs plugin
30
+ */
31
+ function run() {
32
+ if ( Util_Admin::get_current_wp_page() == 'w3tc_dashboard' )
33
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
34
+
35
+ add_action( 'w3tc_widget_setup', array(
36
+ $this,
37
+ 'wp_dashboard_setup'
38
+ ) );
39
+ add_action( 'w3tc_network_dashboard_setup', array(
40
+ $this,
41
+ 'wp_dashboard_setup'
42
+ ) );
43
+
44
+ // Configure authorize and have_zone
45
+ $this->_setup( $this->_config );
46
+
47
+ if ( $this->have_zone && $this->authorized && isset( $_GET['page'] ) && strpos( $_GET['page'], 'w3tc_dashboard' ) !== false ) {
48
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
49
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNAPresentation.php';
50
+ $authorization_key = $this->_config->get_string( 'cdn.maxcdn.authorization_key' );
51
+ $alias = $consumerkey = $consumersecret = '';
52
+ $keys = explode( '+', $authorization_key );
53
+ if ( sizeof( $keys ) == 3 )
54
+ list( $alias, $consumerkey, $consumersecret ) = $keys;
55
+
56
+ $this->api = new \NetDNA( $alias, $consumerkey, $consumersecret );
57
+
58
+ add_action( 'admin_head', array( $this, 'admin_head' ) );
59
+ }
60
+ }
61
+
62
+ function admin_head() {
63
+ $zone_id = $this->_config->get_string( 'cdn.maxcdn.zone_id' );
64
+ try {
65
+ $zone_info = $this->api->get_pull_zone( $zone_id );
66
+
67
+ if ( !$zone_info )
68
+ return;
69
+ $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id );
70
+
71
+ if ( !isset( $filetypes['filetypes'] ) )
72
+ return;
73
+ } catch ( \Exception $ex ) {
74
+ return;
75
+ }
76
+ $filetypes = $filetypes['filetypes'];
77
+ $group_hits = \NetDNAPresentation::group_hits_per_filetype_group( $filetypes );
78
+
79
+ $list = array();
80
+ $colors = array();
81
+ foreach ( $group_hits as $group => $hits ) {
82
+ $list[] = sprintf( "['%s', %d]", $group, $hits );
83
+ $colors[] = '\'' . \NetDNAPresentation::get_file_group_color( $group ) . '\'';
84
+ }
85
+ ?>
86
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
87
+ <script type="text/javascript">
88
+ google.load("visualization", "1", {packages:["corechart"]});
89
+ google.setOnLoadCallback(drawChart);
90
+ function drawChart() {
91
+ var data = google.visualization.arrayToDataTable([
92
+ ['Filetype', 'Hits'],<?php
93
+ echo " ", implode( ',', $list );
94
+ ?>
95
+ ]);
96
+ var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
97
+ var options = {colors: [<?php echo implode( ',', $colors ) ?>]};
98
+ chart.draw(data, options);
99
+ }
100
+ </script>
101
+ <?php
102
+ }
103
+
104
+ /**
105
+ * Dashboard setup action
106
+ *
107
+ * @return void
108
+ */
109
+ function wp_dashboard_setup() {
110
+ Util_Widget::add( 'w3tc_maxcdn',
111
+ '<div class="w3tc-widget-maxcdn-logo"></div>',
112
+ array( $this, 'widget_maxcdn' ),
113
+ Util_Ui::admin_url( 'admin.php?page=w3tc_cdn' ),
114
+ 'normal' );
115
+ }
116
+
117
+ /**
118
+ * Loads and configures NetDNA widget to be used in WP Dashboards.
119
+ *
120
+ * @param unknown $widget_id
121
+ * @param array $form_inputs
122
+ */
123
+ function widget_maxcdn( $widget_id, $form_inputs = array() ) {
124
+
125
+ $authorized = $this->authorized;
126
+ $have_zone = $this->have_zone;
127
+ $error = '';
128
+ $no_zone = $this->_config->get_integer( 'cdn.maxcdn.zone_id' ) == 0;
129
+ $is_sealed = $this->_sealed;
130
+ $pull_zones = array();
131
+ $zone_info = false;
132
+ if ( $this->authorized && $this->have_zone ) {
133
+ $zone_id = $this->_config->get_integer( 'cdn.maxcdn.zone_id' );
134
+
135
+ try{
136
+ $zone_info = $this->api->get_pull_zone( $zone_id );
137
+ } catch ( \Exception $ex ) {
138
+ $zone_info = false;
139
+ $error = $ex->getMessage();
140
+ }
141
+
142
+ if ( $zone_info ) {
143
+ $content_zone = $zone_info['name'];
144
+ try {
145
+ $summary = $this->api->get_stats_per_zone( $zone_id );
146
+ $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id );
147
+ $popular_files = $this->api->get_list_of_popularfiles_per_zone( $zone_id );
148
+ $popular_files = \NetDNAPresentation::format_popular( $popular_files );
149
+ $popular_files = array_slice( $popular_files, 0 , 5 );
150
+ $account = $this->api->get_account();
151
+ $account_status = \NetDNAPresentation::get_account_status( $account['status'] );
152
+ include W3TC_INC_WIDGET_DIR . '/maxcdn.php';
153
+ } catch ( \Exception $ex ) {
154
+ $error = $ex->getMessage();
155
+ try {
156
+ $pull_zones = $this->api->get_zones_by_url( home_url() );
157
+ } catch ( \Exception $ex ) {}
158
+ include W3TC_INC_WIDGET_DIR . '/maxcdn_signup.php';
159
+ }
160
+ } else {
161
+ try {
162
+ $pull_zones = $this->api->get_zones_by_url( home_url() );
163
+ } catch ( \Exception $ex ) {}
164
+ include W3TC_INC_WIDGET_DIR . '/maxcdn_signup.php';
165
+ }
166
+ } else {
167
+ include W3TC_INC_WIDGET_DIR . '/maxcdn_signup.php';
168
+ }
169
+ }
170
+
171
+ /**
172
+ *
173
+ *
174
+ * @param Config $config
175
+ */
176
+ private function _setup( $config ) {
177
+ $this->authorized = $config->get_string( 'cdn.maxcdn.authorization_key' ) != '' &&
178
+ $config->get_string( 'cdn.engine' ) == 'maxcdn';
179
+ $keys = explode( '+', $config->get_string( 'cdn.maxcdn.authorization_key' ) );
180
+ $this->authorized = $this->authorized && sizeof( $keys ) == 3;
181
+
182
+ $this->have_zone = $config->get_string( 'cdn.maxcdn.zone_id' ) != 0;
183
+ }
184
+
185
+ public function enqueue() {
186
+ wp_enqueue_style( 'w3tc-widget' );
187
+ wp_enqueue_script( 'w3tc-metadata' );
188
+ wp_enqueue_script( 'w3tc-widget' );
189
+ }
190
+ }
Cdn_Plugin_WidgetNetDna.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+ /**
4
+ * W3 NetDNA Widget
5
+ */
6
+
7
+
8
+
9
+ /**
10
+ * Class Cdn_Plugin_WidgetNetDna
11
+ */
12
+ class Cdn_Plugin_WidgetNetDna {
13
+ private $authorized;
14
+ private $have_zone;
15
+ private $_sealed;
16
+
17
+ /**
18
+ *
19
+ *
20
+ * @var NetDNA
21
+ */
22
+ private $api;
23
+
24
+ /**
25
+ * Config
26
+ */
27
+ private $_config = null;
28
+
29
+ function __construct() {
30
+ $this->_config = Dispatcher::config();
31
+ }
32
+
33
+ /**
34
+ * Runs plugin
35
+ */
36
+ function run() {
37
+ if ( Util_Admin::get_current_wp_page() == 'w3tc_dashboard' )
38
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
39
+ add_action( 'w3tc_widget_setup', array(
40
+ $this,
41
+ 'wp_dashboard_setup'
42
+ ) );
43
+ add_action( 'w3tc_network_dashboard_setup', array(
44
+ $this,
45
+ 'wp_dashboard_setup'
46
+ ) );
47
+
48
+ // Configure authorize and have_zone
49
+ $this->_setup( $this->_config );
50
+
51
+ if ( $this->have_zone && $this->authorized && isset( $_GET['page'] ) && strpos( $_GET['page'], 'w3tc_dashboard' ) !== false ) {
52
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNA.php';
53
+ require_once W3TC_LIB_NETDNA_DIR . '/NetDNAPresentation.php';
54
+ $authorization_key = $this->_config->get_string( 'cdn.netdna.authorization_key' );
55
+ $alias = $consumerkey = $consumersecret = '';
56
+
57
+ $keys = explode( '+', $authorization_key );
58
+ if ( sizeof( $keys ) == 3 )
59
+ list( $alias, $consumerkey, $consumersecret ) = $keys;
60
+
61
+ $this->api = new \NetDNA( $alias, $consumerkey, $consumersecret );
62
+ add_action( 'admin_head', array( $this, 'admin_head' ) );
63
+ }
64
+ }
65
+
66
+ function admin_head() {
67
+ $zone_id = $this->_config->get_string( 'cdn.netdna.zone_id' );
68
+ try {
69
+ $zone_info = $this->api->get_pull_zone( $zone_id );
70
+
71
+ if ( !$zone_info )
72
+ return;
73
+ $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id );
74
+
75
+ if ( !isset( $filetypes['filetypes'] ) )
76
+ return;
77
+ } catch ( \Exception $ex ) {
78
+ return;
79
+ }
80
+
81
+ $filetypes = $filetypes['filetypes'];
82
+ $group_hits = \NetDNAPresentation::group_hits_per_filetype_group( $filetypes );
83
+
84
+ $list = array();
85
+ $colors = array();
86
+ foreach ( $group_hits as $group => $hits ) {
87
+ $list[] = sprintf( "['%s', %d]", $group, $hits );
88
+ $colors[] = '\'' . \NetDNAPresentation::get_file_group_color( $group ) . '\'';
89
+ }
90
+ ?>
91
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
92
+ <script type="text/javascript">
93
+ google.load("visualization", "1", {packages:["corechart"]});
94
+ google.setOnLoadCallback(drawChart);
95
+ function drawChart() {
96
+ var data = google.visualization.arrayToDataTable([
97
+ ['Filetype', 'Hits'],<?php
98
+ echo " ", implode( ',', $list );
99
+ ?>
100
+ ]);
101
+ var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
102
+ var options = {colors: [<?php echo implode( ',', $colors ) ?>]};
103
+ chart.draw(data, options);
104
+ }
105
+ </script>
106
+ <?php
107
+ }
108
+
109
+ /**
110
+ * Dashboard setup action
111
+ *
112
+ * @return void
113
+ */
114
+ function wp_dashboard_setup() {
115
+ Util_Widget::add( 'w3tc_netdna',
116
+ '<div class="w3tc-widget-netdna-logo"></div>',
117
+ array( $this, 'widget_netdna' ),
118
+ Util_Ui::admin_url( 'admin.php?page=w3tc_cdn' ),
119
+ 'normal' );
120
+ }
121
+
122
+ /**
123
+ * Loads and configures NetDNA widget to be used in WP Dashboards.
124
+ *
125
+ * @param unknown $widget_id
126
+ * @param array $form_inputs
127
+ */
128
+ function widget_netdna( $widget_id, $form_inputs = array() ) {
129
+ $authorized = $this->authorized;
130
+ $have_zone = $this->have_zone;
131
+ $is_sealed = $this->_sealed;
132
+ $error = '';
133
+ $pull_zones = array();
134
+ $zone_info = false;
135
+ if ( $authorized && $have_zone ) {
136
+ $zone_id = $this->_config->get_integer( 'cdn.netdna.zone_id' );
137
+ try{
138
+ $zone_info = $this->api->get_pull_zone( $zone_id );
139
+ } catch ( \Exception $ex ) {
140
+ $error = $ex->getMessage();
141
+ $zone_info = false;
142
+ }
143
+ if ( $zone_info ) {
144
+ $content_zone = $zone_info['name'];
145
+ try{
146
+ $summary = $this->api->get_stats_per_zone( $zone_id );
147
+ $filetypes = $this->api->get_list_of_file_types_per_zone( $zone_id );
148
+ $popular_files = $this->api->get_list_of_popularfiles_per_zone( $zone_id );
149
+ $popular_files = \NetDNAPresentation::format_popular( $popular_files );
150
+ $popular_files = array_slice( $popular_files, 0 , 5 );
151
+ $account = $this->api->get_account();
152
+ $account_status = \NetDNAPresentation::get_account_status( $account['status'] );
153
+ include W3TC_INC_WIDGET_DIR . '/netdna.php';
154
+ } catch ( \Exception $ex ) {
155
+ try {
156
+ $pull_zones = $this->api->get_zones_by_url( home_url() );
157
+ } catch ( \Exception $ex ) {}
158
+ $error = $ex->getMessage();
159
+ include W3TC_INC_WIDGET_DIR . '/netdna_signup.php';
160
+ }
161
+ } else {
162
+ try {
163
+ $pull_zones = $this->api->get_zones_by_url( home_url() );
164
+ } catch ( \Exception $ex ) {}
165
+ include W3TC_INC_WIDGET_DIR . '/netdna_signup.php';
166
+ }
167
+ } else {
168
+ include W3TC_INC_WIDGET_DIR . '/netdna_signup.php';
169
+ }
170
+ }
171
+
172
+ /**
173
+ *
174
+ *
175
+ * @param Config $config
176
+ */
177
+ private function _setup( $config ) {
178
+ $this->authorized = $config->get_string( 'cdn.netdna.authorization_key' ) != '' &&
179
+ $config->get_string( 'cdn.engine' ) == 'netdna';
180
+ $keys = explode( '+', $config->get_string( 'cdn.netdna.authorization_key' ) );
181
+ $this->authorized = $this->authorized && sizeof( $keys ) == 3;
182
+
183
+ $this->have_zone = $config->get_string( 'cdn.netdna.zone_id' ) != 0;
184
+ }
185
+
186
+ public function enqueue() {
187
+ wp_enqueue_style( 'w3tc-widget' );
188
+ wp_enqueue_script( 'w3tc-metadata' );
189
+ wp_enqueue_script( 'w3tc-widget' );
190
+ }
191
+ }
Cdn_RackSpaceCdn_AdminActions.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpaceCdn_AdminActions {
7
+ function w3tc_cdn_rackspace_cdn_domains_reload() {
8
+ $c = Dispatcher::config();
9
+ $core = Dispatcher::component( 'Cdn_Core' );
10
+ $cdn = $core->get_cdn();
11
+
12
+ try {
13
+ // try to obtain CNAMEs
14
+ $domains = $cdn->service_domains_get();
15
+ } catch ( \Exception $ex ) {
16
+ Util_Admin::redirect_with_custom_messages2( array(
17
+ 'errors' => array( 'Failed to obtain CNAMEs: ' . $ex->getMessage() )
18
+ ), true );
19
+ return;
20
+ }
21
+
22
+ $c->set( 'cdn.rackspace_cdn.domains', $domains );
23
+ $c->save();
24
+
25
+ Util_Admin::redirect_with_custom_messages2( array(
26
+ 'notes' => array( 'CNAMEs are reloaded successfully' )
27
+ ), true );
28
+ }
29
+ }
Cdn_RackSpaceCdn_Page.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_RackSpaceCdn_Page {
5
+ // called from plugin-admin
6
+ static public function w3tc_admin_actions( $handlers ) {
7
+ $handlers['cdn_rackspace_cdn'] = 'Cdn_RackSpaceCdn_AdminActions';
8
+
9
+ return $handlers;
10
+ }
11
+
12
+ // called from plugin-admin
13
+ static public function admin_print_scripts_w3tc_cdn() {
14
+ wp_enqueue_script( 'w3tc_cdn_rackspace',
15
+ plugins_url( 'Cdn_RackSpaceCdn_Page_View.js', W3TC_FILE ),
16
+ array( 'jquery' ), '1.0' );
17
+ }
18
+
19
+
20
+
21
+ static public function w3tc_settings_cdn_boxarea_configuration() {
22
+ $config = Dispatcher::config();
23
+ $api_key = $config->get_string( 'cdn.rackspace_cdn.api_key' );
24
+ $authorized = !empty( $api_key );
25
+
26
+ $access_url_full = '';
27
+ if ( $authorized ) {
28
+ $p = $config->get_string( 'cdn.rackspace_cdn.service.protocol' );
29
+ $access_url_full =
30
+ ( $p == 'https' ? 'https://' : 'http://' ) .
31
+ $config->get_string( 'cdn.rackspace_cdn.service.access_url' );
32
+ }
33
+
34
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Page_View.php';
35
+ }
36
+ }
Cdn_RackSpaceCdn_Page_View.js ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tc_rackspace_resize(o) {
3
+ o.options.height = jQuery('.w3tc_cdn_rackspace_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+
8
+ function w3tc_rackspace_created(o) {
9
+ w3tc_rackspace_resize(o);
10
+ w3tc_rackspace_check_service_state();
11
+ }
12
+
13
+
14
+
15
+ function w3tc_rackspace_check_service_state() {
16
+ var service_id = jQuery('input[name="service_id"]').val();
17
+ var access_token = jQuery('input[name="access_token"]').val();
18
+ var access_region_descriptor = jQuery('input[name="access_region_descriptor"]').val();
19
+
20
+ jQuery.post(ajaxurl,
21
+ {
22
+ 'action': 'w3tc_ajax',
23
+ '_wpnonce': w3tc_nonce,
24
+ 'service_id': service_id,
25
+ 'access_token': access_token,
26
+ 'access_region_descriptor': access_region_descriptor,
27
+ 'w3tc_action': 'cdn_rackspace_service_get_state'
28
+ }, function(data) {
29
+ var state = 'unknown';
30
+ if (data && data['status'])
31
+ status = data['status'];
32
+
33
+ jQuery('.w3tc_rackspace_created_status').html(status);
34
+
35
+ if (status == 'deployed')
36
+ w3tc_rackspace_service_created_done(data);
37
+ else
38
+ setTimeout(w3tc_rackspace_check_service_state, 5000);
39
+ }, 'json'
40
+ ).fail(function() {
41
+ jQuery('.w3tc_rackspace_created_state').html('Failed to obtain state');
42
+ setTimeout(w3tc_rackspace_check_service_state, 5000);
43
+ });
44
+ }
45
+
46
+
47
+
48
+ function w3tc_rackspace_service_created_done(data) {
49
+ jQuery('.w3tc_rackspace_cname').html(data['cname']);
50
+ jQuery('.w3tc_rackspace_access_url').html(data['access_url']);
51
+ jQuery('.w3tc_rackspace_created_in_progress').css('display', 'none');
52
+ jQuery('.w3tc_rackspace_created_done').css('display', '');
53
+
54
+ w3tc_rackspace_resize(W3tc_Lightbox);
55
+ }
56
+
57
+
58
+
59
+ $('body')
60
+ /**
61
+ * Authorize popup
62
+ */
63
+ .on('click', '.w3tc_cdn_rackspace_authorize', function() {
64
+ W3tc_Lightbox.open({
65
+ id:'w3tc-overlay',
66
+ close: '',
67
+ width: 800,
68
+ height: 300,
69
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
70
+ '&w3tc_action=cdn_rackspace_intro',
71
+ callback: w3tc_rackspace_resize
72
+ });
73
+ })
74
+
75
+
76
+
77
+ .on('click', '.w3tc_popup_submit', function() {
78
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce;
79
+
80
+ W3tc_Lightbox.load_form(url, '.w3tc_cdn_rackspace_form',
81
+ w3tc_rackspace_resize);
82
+ })
83
+
84
+
85
+
86
+ .on('click', '.w3tc_cdn_rackspace_service_create_done', function() {
87
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
88
+ '&w3tc_action=cdn_rackspace_service_create_done';
89
+
90
+ W3tc_Lightbox.load_form(url, '.w3tc_cdn_rackspace_form',
91
+ w3tc_rackspace_created);
92
+ })
93
+
94
+
95
+
96
+ .on('click', '.w3tc_cdn_rackspace_protocol', function() {
97
+ var protocol = '';
98
+
99
+ $('body').find('.w3tc_cdn_rackspace_protocol').each(function(i) {
100
+ if (!jQuery(this).attr('checked'))
101
+ return;
102
+
103
+ protocol = $(this).val();
104
+ });
105
+
106
+ //alert('ha ' + protocol);
107
+
108
+ $('.w3tc_cdn_rackspace_cname_http').css('display',
109
+ (protocol == 'http' ? '' : 'none'));
110
+ $('.w3tc_cdn_rackspace_cname_https').css('display',
111
+ (protocol == 'https' ? '' : 'none'));
112
+ })
113
+
114
+
115
+
116
+ /**
117
+ * CNAMEs popup
118
+ */
119
+ .on('click', '.w3tc_cdn_rackspace_configure_domains', function() {
120
+ W3tc_Lightbox.open({
121
+ id:'w3tc-overlay',
122
+ close: '',
123
+ width: 1000,
124
+ height: 400,
125
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
126
+ '&w3tc_action=cdn_rackspace_configure_domains',
127
+ callback: function(o) {
128
+ w3tc_rackspace_resize(o);
129
+ w3tc_cdn_cnames_assign();
130
+ }
131
+ });
132
+ })
133
+
134
+
135
+
136
+ .on('click', '.w3tc_cdn_rackspace_configure_domains_done', function() {
137
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
138
+ '&w3tc_action=cdn_rackspace_configure_domains_done';
139
+
140
+ var v = $('.w3tc_cdn_rackspace_form').find('input').each(function(i) {
141
+ var name = $(this).attr('name');
142
+
143
+ if (name)
144
+ url += '&' + encodeURIComponent(name) + '=' +
145
+ encodeURIComponent($(this).val());
146
+ });
147
+
148
+ W3tc_Lightbox.load(url, function(o) {
149
+ w3tc_rackspace_resize(o);
150
+ w3tc_cdn_cnames_assign();
151
+ });
152
+ })
153
+
154
+
155
+
156
+ .on('size_change', '#cdn_cname_add', function() {
157
+ w3tc_rackspace_resize(W3tc_Lightbox);
158
+ })
159
+ });
Cdn_RackSpaceCdn_Page_View.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <tr>
9
+ <th style="width: 300px;"><label><?php _e( 'Authorize:', 'w3-total-cache' ); ?></label></th>
10
+ <td>
11
+ <?php if ( $authorized ): ?>
12
+ <input class="w3tc_cdn_rackspace_authorize button" type="button"
13
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>" />
14
+ <?php else: ?>
15
+ <input class="w3tc_cdn_rackspace_authorize button" type="button"
16
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>" />
17
+ <?php endif ?>
18
+ </td>
19
+ </tr>
20
+
21
+ <?php if ( $authorized ): ?>
22
+ <tr>
23
+ <th><?php _e( 'Username:', 'w3-total-cache' ); ?></th>
24
+ <td class="w3tc_config_value_text">
25
+ <?php echo $config->get_string( 'cdn.rackspace_cdn.user_name' ) ?>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <th><?php _e( 'Region:', 'w3-total-cache' ); ?></th>
30
+ <td class="w3tc_config_value_text">
31
+ <?php echo $config->get_string( 'cdn.rackspace_cdn.region' ) ?>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <th><?php _e( 'Service:', 'w3-total-cache' ); ?></th>
36
+ <td class="w3tc_config_value_text">
37
+ <?php echo $config->get_string( 'cdn.rackspace_cdn.service.name' ) ?>
38
+ </td>
39
+ </tr>
40
+ <tr>
41
+ <th><label><?php _e( 'CDN host (CNAME target):', 'w3-total-cache' ); ?></label></th>
42
+ <td class="w3tc_config_value_text">
43
+ <?php echo $access_url_full ?>
44
+ </td>
45
+ </tr>
46
+ <?php if ( $config->get_string( 'cdn.rackspace_cdn.service.protocol' ) == 'http' ): ?>
47
+ <tr>
48
+ <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
49
+ <td>
50
+ <?php $cnames = $config->get_array( 'cdn.rackspace_cdn.domains' ); include W3TC_INC_DIR . '/options/cdn/common/cnames-readonly.php'; ?>
51
+ <input class="w3tc_cdn_rackspace_configure_domains button" type="button"
52
+ value="<?php _e( 'Configure CNAMEs', 'w3-total-cache' ); ?>" />
53
+ <br />
54
+ <span class="description">
55
+ <?php _e( 'Enter hostname mapped to <acronym>CDN</acronym> host, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>.', 'w3-total-cache' ); ?>
56
+ </span>
57
+ </td>
58
+ </tr>
59
+ <?php else: ?>
60
+ <tr>
61
+ <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
62
+ <td>
63
+ <?php $cnames = $config->get_array( 'cdn.rackspace_cdn.domains' ); include W3TC_INC_DIR . '/options/cdn/common/cnames-readonly.php'; ?>
64
+ <input name="w3tc_cdn_rackspace_cdn_domains_reload"
65
+ class="w3tc-button-save button" type="submit"
66
+ value="<?php _e( 'Reload CNAMEs from RackSpace', 'w3-total-cache' ); ?>" />
67
+ <br />
68
+ <span class="description">
69
+ <?php _e( 'Hostname(s) mapped to <acronym>CDN</acronym> host, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>. You can manage them from RackSpace management console and load here afterwards.', 'w3-total-cache' ); ?>
70
+ </span>
71
+ </td>
72
+ </tr>
73
+ <?php endif ?>
74
+ <tr>
75
+ <th colspan="2">
76
+ <input id="cdn_test"
77
+ class="button {type: 'rackspace_cdn', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}"
78
+ type="button"
79
+ value="<?php _e( 'Test', 'w3-total-cache' ); ?>" />
80
+ <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
81
+ </th>
82
+ </tr>
83
+ <?php endif ?>
Cdn_RackSpaceCdn_Popup.php ADDED
@@ -0,0 +1,547 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpaceCdn_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Cdn_RackSpaceCdn_Popup();
9
+
10
+ add_action( 'w3tc_ajax_cdn_rackspace_intro',
11
+ array( $o, 'w3tc_ajax_cdn_rackspace_intro' ) );
12
+ add_action( 'w3tc_ajax_cdn_rackspace_intro_done',
13
+ array( $o, 'w3tc_ajax_cdn_rackspace_intro_done' ) );
14
+ add_action( 'w3tc_ajax_cdn_rackspace_regions_done',
15
+ array( $o, 'w3tc_ajax_cdn_rackspace_regions_done' ) );
16
+ add_action( 'w3tc_ajax_cdn_rackspace_services_done',
17
+ array( $o, 'w3tc_ajax_cdn_rackspace_services_done' ) );
18
+ add_action( 'w3tc_ajax_cdn_rackspace_service_create_done',
19
+ array( $o, 'w3tc_ajax_cdn_rackspace_service_create_done' ) );
20
+ add_action( 'w3tc_ajax_cdn_rackspace_service_get_state',
21
+ array( $o, 'w3tc_ajax_cdn_rackspace_service_get_state' ) );
22
+ add_action( 'w3tc_ajax_cdn_rackspace_service_created_done',
23
+ array( $o, 'w3tc_ajax_cdn_rackspace_service_created_done' ) );
24
+ add_action( 'w3tc_ajax_cdn_rackspace_service_actualize_done',
25
+ array( $o, 'w3tc_ajax_cdn_rackspace_service_actualize_done' ) );
26
+
27
+ add_action( 'w3tc_ajax_cdn_rackspace_configure_domains',
28
+ array( $o, 'w3tc_ajax_cdn_rackspace_configure_domains' ) );
29
+ add_action( 'w3tc_ajax_cdn_rackspace_configure_domains_done',
30
+ array( $o, 'w3tc_ajax_cdn_rackspace_configure_domains_done' ) );
31
+
32
+ }
33
+
34
+
35
+
36
+ public function w3tc_ajax_cdn_rackspace_intro() {
37
+ $c = Dispatcher::config();
38
+
39
+ $details = array(
40
+ 'user_name' => $c->get_string( 'cdn.rackspace_cdn.user_name' ),
41
+ 'api_key' => $c->get_string( 'cdn.rackspace_cdn.api_key' )
42
+ );
43
+
44
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Intro.php';
45
+ exit();
46
+ }
47
+
48
+
49
+
50
+ public function w3tc_ajax_cdn_rackspace_intro_done() {
51
+ $this->_render_cdn_rackspace_regions( array(
52
+ 'user_name' => $_REQUEST['user_name'],
53
+ 'api_key' => $_REQUEST['api_key'] ) );
54
+ }
55
+
56
+
57
+
58
+ private function _render_cdn_rackspace_regions( $details ) {
59
+ $user_name = $details['user_name'];
60
+ $api_key = $details['api_key'];
61
+
62
+ try {
63
+ $r = Cdn_RackSpace_Api_Tokens::authenticate( $user_name,
64
+ $api_key );
65
+ } catch ( \Exception $ex ) {
66
+ $details = array(
67
+ 'user_name' => $user_name,
68
+ 'api_key' => $api_key,
69
+ 'error_message' => 'Can\'t authenticate: ' . $ex->getMessage()
70
+ );
71
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Intro.php';
72
+ exit();
73
+ }
74
+
75
+ $r['regions'] = Cdn_RackSpace_Api_Tokens::cdn_services_by_region(
76
+ $r['services'] );
77
+
78
+ $details['access_token'] = $r['access_token'];
79
+ $details['region_descriptors'] = $r['regions'];
80
+ // avoid fights with quotes, magic_quotes may break randomly
81
+ $details['region_descriptors_serialized'] =
82
+ strtr( json_encode( $r['regions'] ), '"\\', '!^' );
83
+
84
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Regions.php';
85
+ exit();
86
+ }
87
+
88
+
89
+
90
+ public function w3tc_ajax_cdn_rackspace_regions_done() {
91
+ $user_name = $_REQUEST['user_name'];
92
+ $api_key = $_REQUEST['api_key'];
93
+ $access_token = $_REQUEST['access_token'];
94
+ $region = Util_Request::get( 'region' );
95
+ $region_descriptors = json_decode(
96
+ strtr( $_REQUEST['region_descriptors'], '!^', '"\\' ), true );
97
+
98
+ if ( !isset( $region_descriptors[$region] ) ) {
99
+ return $this->_render_cdn_rackspace_regions( array(
100
+ 'user_name' => $user_name,
101
+ 'api_key' => $api_key,
102
+ 'error_message' => 'Please select region ' . $region
103
+ ) );
104
+ }
105
+
106
+ $api = new Cdn_RackSpace_Api_Cdn( array(
107
+ 'access_token' => $access_token,
108
+ 'access_region_descriptor' => $region_descriptors[$region],
109
+ 'new_access_required' => ''
110
+ ) );
111
+
112
+ try {
113
+ $services = $api->services();
114
+ } catch ( \Exception $ex ) {
115
+ $details = array(
116
+ 'user_name' => $user_name,
117
+ 'api_key' => $api_key,
118
+ 'error_message' => $ex->getMessage()
119
+ );
120
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Intro.php';
121
+ exit();
122
+ }
123
+
124
+ $details = array(
125
+ 'user_name' => $user_name,
126
+ 'api_key' => $api_key,
127
+ 'access_token' => $access_token,
128
+ 'access_region_descriptor_serialized' =>
129
+ strtr( json_encode( $region_descriptors[$region] ), '"\\', '!^' ),
130
+ 'region' => $region,
131
+ // avoid fights with quotes, magic_quotes may break randomly
132
+ 'services' => $services
133
+ );
134
+
135
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Services.php';
136
+ exit();
137
+ }
138
+
139
+
140
+
141
+ public function w3tc_ajax_cdn_rackspace_services_done() {
142
+ $user_name = $_REQUEST['user_name'];
143
+ $api_key = $_REQUEST['api_key'];
144
+ $access_token = $_REQUEST['access_token'];
145
+ $access_region_descriptor = json_decode(
146
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
147
+ $region = $_REQUEST['region'];
148
+ $service = Util_Request::get( 'service' );
149
+
150
+ if ( !empty( $service ) ) {
151
+ $this->_render_service_actualize( array(
152
+ 'user_name' => $user_name,
153
+ 'api_key' => $api_key,
154
+ 'access_token' => $access_token,
155
+ 'access_region_descriptor_serialized' =>
156
+ strtr( json_encode( $access_region_descriptor ), '"\\', '!^' ),
157
+ 'region' => $region,
158
+ 'service_id' => $service
159
+ ) );
160
+ exit();
161
+ }
162
+
163
+ $home_url = get_home_url();
164
+ $parsed = parse_url( $home_url );
165
+
166
+ $is_https = ( $parsed['scheme'] == 'https' );
167
+
168
+ $details = array(
169
+ 'user_name' => $user_name,
170
+ 'api_key' => $api_key,
171
+ 'access_token' => $access_token,
172
+ 'access_region_descriptor_serialized' =>
173
+ strtr( json_encode( $access_region_descriptor ), '"\\', '!^' ),
174
+ 'region' => $region,
175
+ 'name' => '',
176
+ 'protocol' => ( $is_https ? 'https' : 'http' ),
177
+ 'cname_http' => '',
178
+ 'cname_http_style' => ( $is_https ? 'display: none' : '' ),
179
+ 'cname_https_prefix' => '',
180
+ 'cname_https_style' => ( $is_https ? '' : 'display: none' ),
181
+ 'origin' => Util_Environment::home_url_host()
182
+ );
183
+
184
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Service_Create.php';
185
+ exit();
186
+ }
187
+
188
+
189
+
190
+ public function w3tc_ajax_cdn_rackspace_service_create_done() {
191
+ $user_name = $_REQUEST['user_name'];
192
+ $api_key = $_REQUEST['api_key'];
193
+ $access_token = $_REQUEST['access_token'];
194
+ $access_region_descriptor = json_decode(
195
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
196
+ $region = $_REQUEST['region'];
197
+
198
+ $name = $_REQUEST['name'];
199
+ $protocol = $_REQUEST['protocol'];
200
+ $cname_http = $_REQUEST['cname_http'];
201
+ $cname_https_prefix = $_REQUEST['cname_https_prefix'];
202
+
203
+ $is_https = ( $protocol == 'https' );
204
+ $cname = ( $is_https ? $cname_https_prefix : $cname_http );
205
+
206
+ $api = new Cdn_RackSpace_Api_Cdn( array(
207
+ 'access_token' => $access_token,
208
+ 'access_region_descriptor' => $access_region_descriptor,
209
+ 'new_access_required' => ''
210
+ ) );
211
+
212
+ $service_id = null;
213
+ $access_url = null;
214
+
215
+ try {
216
+ $domain = array(
217
+ 'domain' => $cname,
218
+ 'protocol' => ( $is_https ? 'https' : 'http' )
219
+ );
220
+ if ( $is_https )
221
+ $domain['certificate'] = 'shared';
222
+
223
+ $service_id = $api->service_create( array(
224
+ 'name' => $name,
225
+ 'domains' => array( $domain ),
226
+ 'origins' => array(
227
+ array(
228
+ 'origin' => Util_Environment::home_url_host(),
229
+ 'port' => ( $is_https ? 443 : 80 ),
230
+ 'ssl' => $is_https,
231
+ 'hostheadertype' => 'origin',
232
+ 'rules' => array()
233
+ )
234
+ ),
235
+ 'caching' => array(
236
+ array(
237
+ 'name' => 'default',
238
+ 'ttl' => 86400
239
+ )
240
+ )
241
+ ) );
242
+ } catch ( \Exception $ex ) {
243
+ $details = array(
244
+ 'user_name' => $user_name,
245
+ 'api_key' => $api_key,
246
+ 'access_token' => $access_token,
247
+ 'access_region_descriptor_serialized' =>
248
+ strtr( json_encode( $access_region_descriptor ), '"\\', '!^' ),
249
+ 'region' => $region,
250
+ 'name' => $name,
251
+ 'protocol' => ( $is_https ? 'https' : 'http' ),
252
+ 'cname_http' => $cname_http,
253
+ 'cname_http_style' => ( $is_https ? 'display: none' : '' ),
254
+ 'cname_https_prefix' => $cname_https_prefix,
255
+ 'cname_https_style' => ( $is_https ? '' : 'display: none' ),
256
+ 'origin' => Util_Environment::home_url_host(),
257
+ 'error_message' => $ex->getMessage()
258
+ );
259
+
260
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Service_Create.php';
261
+ exit();
262
+ }
263
+
264
+ $details = array(
265
+ 'user_name' => $user_name,
266
+ 'api_key' => $api_key,
267
+ 'access_token' => $access_token,
268
+ 'access_region_descriptor_serialized' =>
269
+ strtr( json_encode( $access_region_descriptor ), '"\\', '!^' ),
270
+ 'region' => $region,
271
+ 'name' => $name,
272
+ 'is_https' => $is_https,
273
+ 'cname' => $cname,
274
+ 'service_id' => $service_id
275
+ );
276
+
277
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Service_Created.php';
278
+ }
279
+
280
+
281
+
282
+ // ajax returning json for js-script about service state
283
+ public function w3tc_ajax_cdn_rackspace_service_get_state() {
284
+ $access_token = $_REQUEST['access_token'];
285
+ $access_region_descriptor = json_decode(
286
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
287
+ $service_id = $_REQUEST['service_id'];
288
+
289
+ $api = new Cdn_RackSpace_Api_Cdn( array(
290
+ 'access_token' => $access_token,
291
+ 'access_region_descriptor' => $access_region_descriptor,
292
+ 'new_access_required' => ''
293
+ ) );
294
+
295
+ $service = $api->service_get( $service_id );
296
+
297
+ $response = array( 'status' => 'Unknown' );
298
+
299
+ if ( isset( $service['status'] ) )
300
+ $response['status'] = $service['status'];
301
+ if ( isset( $service['links_by_rel']['access_url'] ) )
302
+ $response['access_url'] = $service['links_by_rel']['access_url']['href'];
303
+ if ( isset( $service['domains'] ) )
304
+ $response['cname'] = $service['domains'][0]['domain'];
305
+
306
+ // decode to friendly name
307
+ if ( $response['status'] == 'create_in_progress' )
308
+ $response['status'] = 'Creation in progress...';
309
+
310
+ echo json_encode( $response );
311
+ }
312
+
313
+
314
+
315
+ public function w3tc_ajax_cdn_rackspace_service_created_done() {
316
+ $this->_save_config();
317
+ }
318
+
319
+
320
+
321
+ private function _render_service_actualize( $details ) {
322
+ $access_region_descriptor = json_decode(
323
+ strtr( $details['access_region_descriptor_serialized'],
324
+ '!^', '"\\' ), true );
325
+
326
+ $api = new Cdn_RackSpace_Api_Cdn( array(
327
+ 'access_token' => $details['access_token'],
328
+ 'access_region_descriptor' => $access_region_descriptor,
329
+ 'new_access_required' => ''
330
+ ) );
331
+
332
+ $service = null;
333
+ try {
334
+ $service = $api->service_get( $details['service_id'] );
335
+ } catch ( \Exception $ex ) {
336
+ $details['error_message'] = $ex->getMessage();
337
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Intro.php';
338
+ exit();
339
+ }
340
+
341
+ $origin = '';
342
+ $protocol = 'http';
343
+ if ( isset( $service['origins'] ) && $service['origins'][0]['origin'] ) {
344
+ $protocol = $service['origins'][0]['ssl'] ? 'https' : 'http';
345
+ $origin = $service['origins'][0]['origin'];
346
+ }
347
+
348
+ $details['name'] = $service['name'];
349
+ $details['protocol'] = $protocol;
350
+ $details['origin'] = array(
351
+ 'current' => $origin,
352
+ 'new' => Util_Environment::home_url_host()
353
+ );
354
+
355
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Service_Actualize.php';
356
+ exit();
357
+ }
358
+
359
+
360
+
361
+ public function w3tc_ajax_cdn_rackspace_service_actualize_done() {
362
+ $user_name = $_REQUEST['user_name'];
363
+ $api_key = $_REQUEST['api_key'];
364
+ $access_token = $_REQUEST['access_token'];
365
+ $access_region_descriptor = json_decode(
366
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
367
+ $region = $_REQUEST['region'];
368
+ $service_id = $_REQUEST['service_id'];
369
+
370
+ $api = new Cdn_RackSpace_Api_Cdn( array(
371
+ 'access_token' => $access_token,
372
+ 'access_region_descriptor' => $access_region_descriptor,
373
+ 'new_access_required' => ''
374
+ ) );
375
+
376
+ try {
377
+ $service = $api->service_get( $service_id );
378
+
379
+ $is_https = false;
380
+ $origin = '';
381
+ if ( isset( $service['origins'] ) && $service['origins'][0]['ssl'] ) {
382
+ $is_https = $service['origins'][0]['ssl'];
383
+ $origin = $service['origins'][0]['origin'];
384
+ }
385
+
386
+ $new_origin = Util_Environment::home_url_host();
387
+ if ( $origin != $new_origin ) {
388
+ $api->service_set( $service_id,
389
+ array( array(
390
+ 'op' => 'replace',
391
+ 'path' => '/origins',
392
+ 'value' => array( array(
393
+ 'origin' => $new_origin,
394
+ 'port' => ( $is_https ? 443 : 80 ),
395
+ 'ssl' => $is_https,
396
+ 'hostheadertype' => 'origin',
397
+ 'rules' => array()
398
+ ) ) ) ) );
399
+ }
400
+ } catch ( \Exception $ex ) {
401
+ $details = array(
402
+ 'user_name' => $user_name,
403
+ 'api_key' => $api_key,
404
+ 'error_message' => $ex->getMessage()
405
+ );
406
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_Intro.php';
407
+ exit();
408
+ }
409
+
410
+ $this->_save_config();
411
+ }
412
+
413
+
414
+
415
+ private function _save_config() {
416
+ $user_name = $_REQUEST['user_name'];
417
+ $api_key = $_REQUEST['api_key'];
418
+ $access_token = $_REQUEST['access_token'];
419
+ $access_region_descriptor = json_decode(
420
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
421
+ $region = $_REQUEST['region'];
422
+ $service_id = $_REQUEST['service_id'];
423
+
424
+ $api = new Cdn_RackSpace_Api_Cdn( array(
425
+ 'access_token' => $access_token,
426
+ 'access_region_descriptor' => $access_region_descriptor,
427
+ 'new_access_required' => ''
428
+ ) );
429
+
430
+ $service = $api->service_get( $service_id );
431
+
432
+ $access_url = $service['links_by_rel']['access_url']['href'];
433
+ $protocol = 'http';
434
+ $domain = '';
435
+
436
+ if ( isset( $service['domains'] ) && $service['domains'][0]['protocol'] ) {
437
+ $protocol = $service['domains'][0]['protocol'];
438
+ $domain = $service['domains'][0]['domain'];
439
+ }
440
+
441
+ $c = Dispatcher::config();
442
+
443
+ $c->set( 'cdn.rackspace_cdn.user_name', $user_name );
444
+ $c->set( 'cdn.rackspace_cdn.api_key', $api_key );
445
+ $c->set( 'cdn.rackspace_cdn.region', $region );
446
+ $c->set( 'cdn.rackspace_cdn.service.name', $service['name'] );
447
+ $c->set( 'cdn.rackspace_cdn.service.id', $service_id );
448
+ $c->set( 'cdn.rackspace_cdn.service.access_url', $access_url );
449
+ $c->set( 'cdn.rackspace_cdn.service.protocol', $protocol );
450
+
451
+ if ( $protocol != 'https' )
452
+ $c->set( 'cdn.rackspace_cdn.domains', array( $domain ) );
453
+
454
+ $c->save();
455
+
456
+ // reset calculated state
457
+ $state = Dispatcher::config_state();
458
+ $state->set( 'cdn.rackspace_cdn.access_state', '' );
459
+ $state->save();
460
+
461
+ $postfix = Util_Admin::custom_message_id( array(),
462
+ array(
463
+ 'cdn_configuration_saved' =>
464
+ 'CDN credentials are saved successfully' ) );
465
+ echo 'Location admin.php?page=w3tc_cdn&' . $postfix;
466
+ exit();
467
+ }
468
+
469
+
470
+
471
+ /**
472
+ * CNAMEs popup
473
+ */
474
+ public function w3tc_ajax_cdn_rackspace_configure_domains() {
475
+ $this->render_configure_domains_form();
476
+ exit();
477
+ }
478
+
479
+
480
+
481
+ public function w3tc_ajax_cdn_rackspace_configure_domains_done() {
482
+ $details = array(
483
+ 'cnames' => Util_Request::get_array( 'cdn_cnames' )
484
+ );
485
+
486
+ $core = Dispatcher::component( 'Cdn_Core' );
487
+ $cdn = $core->get_cdn();
488
+
489
+ try {
490
+ // try to obtain CNAMEs
491
+ $cdn->service_domains_set( $details['cnames'] );
492
+
493
+ $c = Dispatcher::config();
494
+ $c->set( 'cdn.rackspace_cdn.domains', $details['cnames'] );
495
+ $c->save();
496
+
497
+ $postfix = Util_Admin::custom_message_id( array(),
498
+ array( 'cdn_cnames_saved' => 'CNAMEs are saved successfully' ) );
499
+ echo 'Location admin.php?page=w3tc_cdn&' . $postfix;
500
+ exit();
501
+ } catch ( \Exception $ex ) {
502
+ $details['error_message'] = $ex->getMessage();
503
+ }
504
+
505
+ $this->render_configure_domains_form( $details );
506
+ exit();
507
+ }
508
+
509
+
510
+
511
+ private function render_configure_domains_form( $details = array() ) {
512
+ if ( isset( $details['cnames'] ) )
513
+ $cnames = $details['cnames'];
514
+ else {
515
+ $core = Dispatcher::component( 'Cdn_Core' );
516
+ $cdn = $core->get_cdn();
517
+
518
+ try {
519
+ // try to obtain CNAMEs
520
+ $cnames = $cdn->service_domains_get();
521
+ } catch ( \Exception $ex ) {
522
+ $details['error_message'] = $ex->getMessage();
523
+ $cnames = array();
524
+ }
525
+ }
526
+
527
+ include W3TC_DIR . '/Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php';
528
+ }
529
+
530
+
531
+
532
+ private function render_service_value_change( $details, $field ) {
533
+ Util_Ui::hidden( '', $field, $details[$field]['new'] );
534
+
535
+ if ( !isset( $details[$field]['current'] ) ||
536
+ $details[$field]['current'] == $details[$field]['new'] )
537
+ echo htmlspecialchars( $details[$field]['new'] );
538
+ else {
539
+ echo 'currently set to <strong>' .
540
+ htmlspecialchars( empty( $details[$field]['current'] ) ?
541
+ '<empty>' : $details[$field]['current'] ) .
542
+ '</strong><br />';
543
+ echo 'will be changed to <strong>' .
544
+ htmlspecialchars( $details[$field]['new'] ) . '</strong><br />';
545
+ }
546
+ }
547
+ }
Cdn_RackSpaceCdn_Popup_View_ConfigureDomains.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px" class="w3tc_cdn_rackspace_form">
8
+ <?php
9
+ if ( !empty( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header( __( 'CNAMEs to use', 'w3-total-cache' ) ); ?>
14
+ <?php $cname_class = 'w3tc-ignore-change'; include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
15
+ <br />
16
+ <span class="description"><?php _e( 'Enter hostname mapped to <acronym>CDN</acronym> host, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>.', 'w3-total-cache' ); ?></span>
17
+
18
+ <p class="submit">
19
+ <input type="button"
20
+ class="w3tc_cdn_rackspace_configure_domains_done w3tc-button-save button-primary"
21
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
22
+ </p>
23
+ <?php Util_Ui::postbox_footer(); ?>
24
+ </div>
25
+ </form>
Cdn_RackSpaceCdn_Popup_View_Intro.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_cdn_rackspace_form" method="post" style="padding: 20px">
8
+ <?php Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_intro_done' ); ?>
9
+ <?php
10
+ if ( isset( $details['error_message'] ) )
11
+ echo '<div class="error">' . $details['error_message'] . '</div>';
12
+ ?>
13
+ <div class="metabox-holder">
14
+ <?php Util_Ui::postbox_header(
15
+ __( 'Your RackSpace API key', 'w3-total-cache' ) ); ?>
16
+ <table class="form-table">
17
+ <tr>
18
+ <th><?php _e( 'Username:', 'w3-total-cache' ) ?></td>
19
+ <td>
20
+ <input name="user_name" type="text" class="w3tc-ignore-change"
21
+ style="width: 100px"
22
+ value="<?php echo esc_attr( $details['user_name'] ) ?>" />
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <th><?php _e( '<acronym title="Application Programming Interface">API</acronym> key:', 'w3-total-cache' ); ?></td>
27
+ <td>
28
+ <input name="api_key" type="text" class="w3tc-ignore-change"
29
+ style="width: 550px"
30
+ value="<?php echo esc_attr( $details['api_key'] ) ?>" />
31
+ </td>
32
+ </tr>
33
+ </table>
34
+
35
+ <p class="submit">
36
+ <input type="button"
37
+ class="w3tc_popup_submit w3tc-button-save button-primary"
38
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
39
+ </p>
40
+ <?php Util_Ui::postbox_footer(); ?>
41
+ </div>
42
+ </form>
Cdn_RackSpaceCdn_Popup_View_Regions.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_regions_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'region_descriptors', $details['region_descriptors_serialized'] );
15
+ echo Util_Ui::nonce_field( 'w3tc' );
16
+
17
+ ?>
18
+ <?php
19
+ if ( isset( $details['error_message'] ) )
20
+ echo '<div class="error">' . $details['error_message'] . '</div>';
21
+ ?>
22
+ <div class="metabox-holder">
23
+ <?php Util_Ui::postbox_header( __( 'Select region', 'w3-total-cache' ) ); ?>
24
+ <table class="form-table">
25
+ <tr>
26
+ <th>Region:</td>
27
+ <td>
28
+ <?php foreach ( $details['region_descriptors'] as $region => $region_details ): ?>
29
+ <label>
30
+ <input name="region" type="radio" class="w3tc-ignore-change"
31
+ value="<?php echo $region ?>" />
32
+ <?php echo $region_details['name'] ?>
33
+ </label><br />
34
+ <?php endforeach ?>
35
+ </tr>
36
+ </table>
37
+
38
+ <p class="submit">
39
+ <input type="button"
40
+ class="w3tc_popup_submit w3tc-button-save button-primary"
41
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
42
+ </p>
43
+ <?php Util_Ui::postbox_footer(); ?>
44
+ </div>
45
+ </form>
Cdn_RackSpaceCdn_Popup_View_Service_Actualize.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_service_actualize_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'access_region_descriptor', $details['access_region_descriptor_serialized'] );
15
+ Util_Ui::hidden( '', 'region', $details['region'] );
16
+ Util_Ui::hidden( '', 'service_id', $details['service_id'] );
17
+ echo Util_Ui::nonce_field( 'w3tc' );
18
+
19
+ if ( isset( $details['error_message'] ) )
20
+ echo '<div class="error">' . $details['error_message'] . '</div>';
21
+ ?>
22
+
23
+ <div class="metabox-holder">
24
+ <?php Util_Ui::postbox_header( __( 'Configure service', 'w3-total-cache' ) ); ?>
25
+ <table class="form-table">
26
+ <tr>
27
+ <th>Name:</th>
28
+ <td><?php echo $details['name'] ?></td>
29
+ </tr>
30
+ <tr>
31
+ <th>Origin host:</th>
32
+ <td><?php $this->render_service_value_change( $details, 'origin' ) ?></td>
33
+ </tr>
34
+ <tr>
35
+ <th>Origin protocol:</th>
36
+ <td><?php echo $details['protocol'] ?><br />
37
+ </td>
38
+ </tr>
39
+ </table>
40
+
41
+ <p class="submit">
42
+ <input type="button"
43
+ class="w3tc_popup_submit w3tc-button-save button-primary"
44
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
45
+ </p>
46
+ <?php Util_Ui::postbox_footer(); ?>
47
+ </div>
48
+ </form>
Cdn_RackSpaceCdn_Popup_View_Service_Create.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
11
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
12
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
13
+ Util_Ui::hidden( '', 'access_region_descriptor', $details['access_region_descriptor_serialized'] );
14
+ Util_Ui::hidden( '', 'region', $details['region'] );
15
+ echo Util_Ui::nonce_field( 'w3tc' );
16
+
17
+ ?>
18
+ <?php
19
+ if ( isset( $details['error_message'] ) )
20
+ echo '<div class="error">' . $details['error_message'] . '</div>';
21
+ ?>
22
+ <div class="metabox-holder">
23
+ <?php Util_Ui::postbox_header( __( 'Create new service', 'w3-total-cache' ) ); ?>
24
+ <table class="form-table" style="width: 100%">
25
+ <tr>
26
+ <th style="width: 150px">Name:</td>
27
+ <td>
28
+ <input name="name" type="text" class="w3tc-ignore-change"
29
+ style="width: 100px"
30
+ value="<?php echo esc_attr( $details['name'] ) ?>" />
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <th style="white-space: nowrap">Traffic Type:</td>
35
+ <td>
36
+ <label>
37
+ <input name="protocol" type="radio"
38
+ class="w3tc-ignore-change w3tc_cdn_rackspace_protocol"
39
+ value="http"
40
+ <?php checked( $details['protocol'], 'http' ) ?> />
41
+ http://
42
+ </label>
43
+ <br />
44
+ <label>
45
+ <input name="protocol" type="radio"
46
+ class="w3tc-ignore-change w3tc_cdn_rackspace_protocol"
47
+ value="https"
48
+ <?php checked( $details['protocol'], 'https' ) ?> />
49
+ https://
50
+ </label>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <th>Origin:</td>
55
+ <td>
56
+ <?php echo $details['origin'] ?>
57
+ </td>
58
+ </tr>
59
+ <tr class="w3tc_cdn_rackspace_cname_http"
60
+ style="<?php echo $details['cname_http_style'] ?>">
61
+ <th style="white-space: nowrap">Primary CNAME:</td>
62
+ <td>
63
+ <input name="cname_http" type="text" class="w3tc-ignore-change"
64
+ style="width: 200px"
65
+ value="<?php echo esc_attr( $details['cname_http'] ) ?>" />
66
+ <br />
67
+ <span class="description">
68
+ <?php _e( 'The domain name through which visitors retrieve content. You will be provided with a target domain to use as an alias for this CNAME', 'w3-total-cache' ); ?>
69
+ </span>
70
+ </td>
71
+ </tr>
72
+ <tr class="w3tc_cdn_rackspace_cname_https"
73
+ style="<?php echo $details['cname_https_style'] ?>">
74
+ <th style="white-space: nowrap">Primary CNAME:</td>
75
+ <td>
76
+ <input name="cname_https_prefix" type="text" class="w3tc-ignore-change"
77
+ style="width: 100px"
78
+ value="<?php echo esc_attr( $details['cname_https_prefix'] ) ?>" />
79
+ <input name="" type="text" readonly="readonly"
80
+ value=".xxxx.secure.raxcdn.com" />
81
+ <br />
82
+ <span class="description">
83
+ <?php _e( 'The name should be a single word, and cannot contain any dots (.).', 'w3-total-cache' ) ?>
84
+ </span>
85
+ </td>
86
+ </tr>
87
+ </table>
88
+
89
+ <p class="submit">
90
+ <input type="button"
91
+ class="w3tc_cdn_rackspace_service_create_done w3tc-button-save button-primary"
92
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
93
+ </p>
94
+ <?php Util_Ui::postbox_footer(); ?>
95
+ </div>
96
+ </form>
Cdn_RackSpaceCdn_Popup_View_Service_Created.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_service_created_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'access_region_descriptor', $details['access_region_descriptor_serialized'] );
15
+ Util_Ui::hidden( '', 'region', $details['region'] );
16
+ Util_Ui::hidden( '', 'service_id', $details['service_id'] );
17
+ echo Util_Ui::nonce_field( 'w3tc' );
18
+
19
+ ?>
20
+ <form class="w3tc_popup_form">
21
+ <div class="metabox-holder">
22
+ <?php Util_Ui::postbox_header( __( 'Succeeded', 'w3-total-cache' ) ); ?>
23
+
24
+ <div style="text-align: center" class="w3tc_rackspace_created_in_progress">
25
+ <div class="spinner" style="float: right; display: block"></div>
26
+ <div style="text-align: left">
27
+ Service <?php echo $details['name'] ?> was successfully created.<br />
28
+ Waiting for RackSpace to finish publishing process.<br />
29
+ <br />
30
+
31
+ Actual state is:
32
+ <strong><span class="w3tc_rackspace_created_status">Initiated</span></strong>
33
+ </div>
34
+ </div>
35
+
36
+ <div style="display: none" class="w3tc_rackspace_created_done">
37
+ <div style="text-align: center">
38
+ <div style="text-align: left">
39
+ Service <?php echo $details['name'] ?> was successfully configured.<br />
40
+ <?php if ( !$is_https ): ?>
41
+ <br />
42
+ Now you need to change DNS records of your domain
43
+ <strong><?php echo $details['cname'] ?></strong> and CNAME it to<br />
44
+ <strong class="w3tc_rackspace_access_url"></strong> to make caching work.
45
+ <?php endif; ?>
46
+ </div>
47
+ </div>
48
+
49
+ <p class="submit">
50
+ <input type="button"
51
+ class="w3tc_popup_submit w3tc-button-save button-primary"
52
+ value="<?php _e( 'Done', 'w3-total-cache' ); ?>" />
53
+ </p>
54
+ </div>
55
+ <?php Util_Ui::postbox_footer(); ?>
56
+ </div>
57
+ </form>
Cdn_RackSpaceCdn_Popup_View_Services.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_services_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'access_region_descriptor', $details['access_region_descriptor_serialized'] );
15
+ Util_Ui::hidden( '', 'region', $details['region'] );
16
+ echo Util_Ui::nonce_field( 'w3tc' );
17
+
18
+ ?>
19
+ <?php
20
+ if ( isset( $details['error_message'] ) )
21
+ echo '<div class="error">' . $details['error_message'] . '</div>';
22
+ ?>
23
+ <div class="metabox-holder">
24
+ <?php Util_Ui::postbox_header( __( 'Select service to use', 'w3-total-cache' ) ); ?>
25
+ <table class="form-table w3tc_popup_form">
26
+ <tr>
27
+ <th>Service:</td>
28
+ <td>
29
+ <?php foreach ( $details['services'] as $service ): ?>
30
+ <label>
31
+ <input name="service" type="radio"
32
+ class="w3tc-ignore-change"
33
+ value="<?php echo $service['id'] ?>" />
34
+ <?php echo $service['name'] ?>
35
+ </label><br />
36
+ <?php endforeach ?>
37
+
38
+ <label>
39
+ <input name="service" type="radio"
40
+ class="w3tc-ignore-change" value="" />
41
+ Add new service
42
+ </label>
43
+ </tr>
44
+ </table>
45
+
46
+ <p class="submit">
47
+ <input type="button"
48
+ class="w3tc_popup_submit w3tc-button-save button-primary"
49
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
50
+ </p>
51
+ <?php Util_Ui::postbox_footer(); ?>
52
+ </div>
53
+ </form>
Cdn_RackSpaceCloudFiles_Page.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Cdn_RackSpaceCloudFiles_Page {
5
+ // called from plugin-admin
6
+ static public function admin_print_scripts_w3tc_cdn() {
7
+ wp_enqueue_script( 'w3tc_cdn_rackspace',
8
+ plugins_url( 'Cdn_RackSpaceCloudFiles_Page_View.js', W3TC_FILE ),
9
+ array( 'jquery' ), '1.0' );
10
+ }
11
+
12
+
13
+
14
+ static public function w3tc_settings_cdn_boxarea_configuration() {
15
+ $config = Dispatcher::config();
16
+ $api_key = $config->get_string( 'cdn.rscf.key' );
17
+ $authorized = !empty( $api_key );
18
+
19
+ $cdn_host_http = '';
20
+ $cdn_host_https = '';
21
+
22
+ if ( $authorized ) {
23
+ try {
24
+ $cdn = Dispatcher::component( 'Cdn_Core' )->get_cdn();
25
+ $cdn_host_http = $cdn->get_host_http();
26
+ $cdn_host_https = $cdn->get_host_https();
27
+ } catch ( \Exception $ex ) {
28
+ $cdn_host_http = 'failed to obtain';
29
+ $cdn_host_https = 'failed to obtain';
30
+ }
31
+ }
32
+
33
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Page_View.php';
34
+ }
35
+ }
Cdn_RackSpaceCloudFiles_Page_View.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tc_rackspace_resize(o) {
3
+ o.options.height = jQuery('.w3tc_cdn_rackspace_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+ $('body')
8
+ .on('click', '.w3tc_cdn_rackspace_authorize', function() {
9
+ W3tc_Lightbox.open({
10
+ id:'w3tc-overlay',
11
+ close: '',
12
+ width: 800,
13
+ height: 300,
14
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
15
+ '&w3tc_action=cdn_rackspace_authenticate',
16
+ callback: w3tc_rackspace_resize
17
+ });
18
+ })
19
+
20
+
21
+
22
+ .on('click', '.w3tc_popup_submit', function() {
23
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce;
24
+
25
+ W3tc_Lightbox.load_form(url, '.w3tc_cdn_rackspace_form',
26
+ w3tc_rackspace_resize);
27
+ })
28
+ });
Cdn_RackSpaceCloudFiles_Page_View.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <tr>
9
+ <th style="width: 300px;"><label><?php _e( 'Authorize:', 'w3-total-cache' ); ?></label></th>
10
+ <td>
11
+ <?php if ( !$authorized ): ?>
12
+ <input class="w3tc_cdn_rackspace_authorize button" type="button"
13
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>" />
14
+ <?php else: ?>
15
+ <input class="w3tc_cdn_rackspace_authorize button" type="button"
16
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>" />
17
+ <?php endif ?>
18
+ </td>
19
+ </tr>
20
+
21
+ <?php if ( $authorized ): ?>
22
+ <tr>
23
+ <th><?php _e( 'Username:', 'w3-total-cache' ); ?></th>
24
+ <td class="w3tc_config_value_text">
25
+ <?php echo $config->get_string( 'cdn.rscf.user' ) ?>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <th><?php _e( 'Region:', 'w3-total-cache' ); ?></th>
30
+ <td class="w3tc_config_value_text">
31
+ <?php echo $config->get_string( 'cdn.rscf.location' ) ?>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <th><?php _e( 'Container:', 'w3-total-cache' ); ?></th>
36
+ <td class="w3tc_config_value_text">
37
+ <?php echo $config->get_string( 'cdn.rscf.container' ) ?>
38
+ </td>
39
+ </tr>
40
+ <tr>
41
+ <th><label><?php _e( 'CDN host (CNAME target):', 'w3-total-cache' ); ?></label></th>
42
+ <td class="w3tc_config_value_text">
43
+ http: <?php echo $cdn_host_http ?><br />
44
+ https: <?php echo $cdn_host_https ?>
45
+ </td>
46
+ </tr>
47
+ <tr>
48
+ <th><label for="cdn_rackspace_ssl"><?php _e( '<acronym title="Secure Sockets Layer">SSL</acronym> support:</label>', 'w3-total-cache' ); ?></th>
49
+ <td>
50
+ <select id="cdn_rackspace_ssl" name="cdn__rscf__ssl">
51
+ <option value="auto"<?php selected( $config->get_string( 'cdn.rscf.ssl' ), 'auto' ); ?>><?php _e( 'Auto (determine connection type automatically)', 'w3-total-cache' ); ?></option>
52
+ <option value="enabled"<?php selected( $config->get_string( 'cdn.rscf.ssl' ), 'enabled' ); ?>><?php _e( 'Enabled (always use SSL)', 'w3-total-cache' ); ?></option>
53
+ <option value="disabled"<?php selected( $config->get_string( 'cdn.rscf.ssl' ), 'disabled' ); ?>><?php _e( 'Disabled (always use HTTP)', 'w3-total-cache' ); ?></option>
54
+ </select>
55
+ <br /><span class="description"><?php _e( 'Some <acronym>CDN</acronym> providers may or may not support <acronym title="Secure Sockets Layer">SSL</acronym>, contact your vendor for more information.', 'w3-total-cache' ); ?></span>
56
+ </td>
57
+ </tr>
58
+ <tr>
59
+ <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
60
+ <td>
61
+ <?php $cnames = $config->get_array( 'cdn.rscf.cname' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
62
+ <br />
63
+ <span class="description">
64
+ <?php _e( 'Enter hostname mapped to <acronym>CDN</acronym> host, this value will replace your site\'s hostname in the <acronym title="Hypertext Markup Language">HTML</acronym>.', 'w3-total-cache' ); ?>
65
+ </span>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <th colspan="2">
70
+ <input id="cdn_test"
71
+ class="button {type: 'highwinds', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}"
72
+ type="button"
73
+ value="<?php _e( 'Test', 'w3-total-cache' ); ?>" />
74
+ <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
75
+ </th>
76
+ </tr>
77
+ <?php endif ?>
Cdn_RackSpaceCloudFiles_Popup.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpaceCloudFiles_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Cdn_RackSpaceCloudFiles_Popup();
9
+
10
+ add_action( 'w3tc_ajax_cdn_rackspace_authenticate',
11
+ array( $o, 'w3tc_ajax_cdn_rackspace_authenticate' ) );
12
+ add_action( 'w3tc_ajax_cdn_rackspace_intro_done',
13
+ array( $o, 'w3tc_ajax_cdn_rackspace_intro_done' ) );
14
+ add_action( 'w3tc_ajax_cdn_rackspace_regions_done',
15
+ array( $o, 'w3tc_ajax_cdn_rackspace_regions_done' ) );
16
+ add_action( 'w3tc_ajax_cdn_rackspace_containers_done',
17
+ array( $o, 'w3tc_ajax_cdn_rackspace_containers_done' ) );
18
+ }
19
+
20
+
21
+
22
+ public function w3tc_ajax_cdn_rackspace_authenticate() {
23
+ $c = Dispatcher::config();
24
+
25
+ $details = array(
26
+ 'user_name' => $c->get_string( 'cdn.rscf.user' ),
27
+ 'api_key' => $c->get_string( 'cdn.rscf.key' )
28
+ );
29
+
30
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Intro.php';
31
+ exit();
32
+ }
33
+
34
+
35
+
36
+ public function w3tc_ajax_cdn_rackspace_intro_done() {
37
+ $user_name = $_REQUEST['user_name'];
38
+ $api_key = $_REQUEST['api_key'];
39
+
40
+ try {
41
+ $r = Cdn_RackSpace_Api_Tokens::authenticate( $user_name,
42
+ $api_key );
43
+ } catch ( \Exception $ex ) {
44
+ $details = array(
45
+ 'user_name' => $user_name,
46
+ 'api_key' => $api_key,
47
+ 'error_message' => 'Can\'t authenticate: ' . $ex->getMessage()
48
+ );
49
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Intro.php';
50
+ exit();
51
+ }
52
+
53
+ $r['regions'] = Cdn_RackSpace_Api_Tokens::cloudfiles_services_by_region(
54
+ $r['services'] );
55
+
56
+ $details = array(
57
+ 'user_name' => $user_name,
58
+ 'api_key' => $api_key,
59
+ 'access_token' => $r['access_token'],
60
+ 'region_descriptors' => $r['regions'],
61
+ // avoid fights with quotes, magic_quotes may break randomly
62
+ 'region_descriptors_serialized' =>
63
+ strtr( json_encode( $r['regions'] ), '"\\', '!^' )
64
+ );
65
+
66
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Regions.php';
67
+ exit();
68
+ }
69
+
70
+
71
+
72
+ public function w3tc_ajax_cdn_rackspace_regions_done() {
73
+ $user_name = $_REQUEST['user_name'];
74
+ $api_key = $_REQUEST['api_key'];
75
+ $access_token = $_REQUEST['access_token'];
76
+ $region = Util_Request::get( 'region' );
77
+ $region_descriptors = json_decode(
78
+ strtr( $_REQUEST['region_descriptors'], '!^', '"\\' ), true );
79
+
80
+ if ( !isset( $region_descriptors[$region] ) ) {
81
+ $details = array(
82
+ 'user_name' => $user_name,
83
+ 'api_key' => $api_key,
84
+ 'error_message' => 'Please select region ' . $region
85
+ );
86
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Intro.php';
87
+ exit();
88
+ }
89
+
90
+ $api = new Cdn_RackSpace_Api_CloudFilesCdn( array(
91
+ 'access_token' => $access_token,
92
+ 'access_region_descriptor' => $region_descriptors[$region],
93
+ 'new_access_required' => ''
94
+ ) );
95
+
96
+ try {
97
+ $containers = $api->containers();
98
+ } catch ( \Exception $ex ) {
99
+ $details = array(
100
+ 'user_name' => $user_name,
101
+ 'api_key' => $api_key,
102
+ 'error_message' => $ex->getMessage()
103
+ );
104
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Intro.php';
105
+ exit();
106
+ }
107
+
108
+ $details = array(
109
+ 'user_name' => $user_name,
110
+ 'api_key' => $api_key,
111
+ 'access_token' => $access_token,
112
+ 'access_region_descriptor_serialized' =>
113
+ strtr( json_encode( $region_descriptors[$region] ), '"\\', '!^' ),
114
+ 'region' => $region,
115
+ // avoid fights with quotes, magic_quotes may break randomly
116
+ 'containers' => $containers
117
+ );
118
+
119
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Containers.php';
120
+ exit();
121
+ }
122
+
123
+
124
+
125
+ public function w3tc_ajax_cdn_rackspace_containers_done() {
126
+ $user_name = $_REQUEST['user_name'];
127
+ $api_key = $_REQUEST['api_key'];
128
+ $access_token = $_REQUEST['access_token'];
129
+ $access_region_descriptor = json_decode(
130
+ strtr( $_REQUEST['access_region_descriptor'], '!^', '"\\' ), true );
131
+ $region = $_REQUEST['region'];
132
+ $container = Util_Request::get( 'container' );
133
+
134
+ $api_files = new Cdn_RackSpace_Api_CloudFiles( array(
135
+ 'access_token' => $access_token,
136
+ 'access_region_descriptor' => $access_region_descriptor,
137
+ 'new_access_required' => ''
138
+ ) );
139
+ $api_cdn = new Cdn_RackSpace_Api_CloudFilesCdn( array(
140
+ 'access_token' => $access_token,
141
+ 'access_region_descriptor' => $access_region_descriptor,
142
+ 'new_access_required' => ''
143
+ ) );
144
+
145
+ try {
146
+ if ( empty( $container ) ) {
147
+ $container_new = $_REQUEST['container_new'];
148
+ if ( empty( $container_new ) )
149
+ throw new \Exception( 'Please select container' );
150
+
151
+ $api_files->container_create( $container_new );
152
+ $api_cdn->container_cdn_enable( $container_new );
153
+ $container = $container_new;
154
+ }
155
+ } catch ( \Exception $ex ) {
156
+ $containers = $api_cdn->containers();
157
+ $details = array(
158
+ 'user_name' => $user_name,
159
+ 'api_key' => $api_key,
160
+ 'access_token' => $access_token,
161
+ // avoid fights with quotes, magic_quotes may break randomly
162
+ 'access_region_descriptor_serialized' =>
163
+ strtr( json_encode( $access_region_descriptor ), '"\\', '!^' ),
164
+ 'region' => $region,
165
+ 'containers' => $containers
166
+ );
167
+ $details['error_message'] = $ex->getMessage();
168
+ include W3TC_DIR . '/Cdn_RackSpaceCloudFiles_Popup_View_Containers.php';
169
+ exit();
170
+ }
171
+
172
+ $c = Dispatcher::config();
173
+
174
+ $c->set( 'cdn.rscf.user', $user_name );
175
+ $c->set( 'cdn.rscf.key', $api_key );
176
+ $c->set( 'cdn.rscf.location', $region );
177
+ $c->set( 'cdn.rscf.container', $container );
178
+ $c->save();
179
+
180
+ // reset calculated state
181
+ $state = Dispatcher::config_state();
182
+ $state->set( 'cdn.rackspace_cf.access_state', '' );
183
+ $state->save();
184
+
185
+ $postfix = Util_Admin::custom_message_id( array(),
186
+ array(
187
+ 'cdn_configuration_saved' =>
188
+ 'CDN credentials are saved successfully' ) );
189
+ echo 'Location admin.php?page=w3tc_cdn&' . $postfix;
190
+ exit();
191
+ }
192
+ }
Cdn_RackSpaceCloudFiles_Popup_View_Containers.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_containers_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'access_region_descriptor', $details['access_region_descriptor_serialized'] );
15
+ Util_Ui::hidden( '', 'region', $details['region'] );
16
+ echo Util_Ui::nonce_field( 'w3tc' );
17
+
18
+ ?>
19
+ <?php
20
+ if ( isset( $details['error_message'] ) )
21
+ echo '<div class="error">' . $details['error_message'] . '</div>';
22
+ ?>
23
+ <div class="metabox-holder">
24
+ <?php Util_Ui::postbox_header( __( 'Select container to use', 'w3-total-cache' ) ); ?>
25
+ <table class="form-table">
26
+ <tr>
27
+ <td>Container:</td>
28
+ <td>
29
+ <?php foreach ( $details['containers'] as $container ): ?>
30
+ <label>
31
+ <input name="container" type="radio" class="w3tc-ignore-change"
32
+ value="<?php echo $container['name'] ?>" />
33
+ <?php echo $container['name'] ?>
34
+ </label><br />
35
+ <?php endforeach ?>
36
+
37
+ <label>
38
+ <input name="container" type="radio" class="w3tc-ignore-change" value=""
39
+ />
40
+ Add new container:
41
+ </label>
42
+ <input name="container_new" type="text" class="w3tc-ignore-change" />
43
+ </tr>
44
+ </table>
45
+
46
+ <p class="submit">
47
+ <input type="button"
48
+ class="w3tc_popup_submit w3tc-button-save button-primary"
49
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
50
+ </p>
51
+ <?php Util_Ui::postbox_footer(); ?>
52
+ </div>
53
+ </form>
Cdn_RackSpaceCloudFiles_Popup_View_Intro.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_cdn_rackspace_form" method="post" style="padding: 20px">
8
+ <?php
9
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_intro_done' );
10
+
11
+ if ( isset( $details['error_message'] ) )
12
+ echo '<div class="error">' . $details['error_message'] . '</div>';
13
+ ?>
14
+ <div class="metabox-holder">
15
+ <?php Util_Ui::postbox_header(
16
+ __( 'Your RackSpace API key', 'w3-total-cache' ) ); ?>
17
+ <table class="form-table">
18
+ <tr>
19
+ <td><?php _e( 'Username:', 'w3-total-cache' ) ?></td>
20
+ <td>
21
+ <input name="user_name" type="text" class="w3tc-ignore-change"
22
+ style="width: 100px"
23
+ value="<?php echo esc_attr( $details['user_name'] ) ?>" />
24
+ </td>
25
+ </tr>
26
+ <tr>
27
+ <td><?php _e( '<acronym title="Application Programming Interface">API</acronym> key:', 'w3-total-cache' ); ?></td>
28
+ <td>
29
+ <input name="api_key" type="text" class="w3tc-ignore-change"
30
+ style="width: 550px"
31
+ value="<?php echo esc_attr( $details['api_key'] ) ?>" />
32
+ </td>
33
+ </tr>
34
+ </table>
35
+
36
+ <p class="submit">
37
+ <input type="button"
38
+ class="w3tc_popup_submit w3tc-button-save button-primary"
39
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
40
+ </p>
41
+ <?php Util_Ui::postbox_footer(); ?>
42
+ </div>
43
+ </form>
Cdn_RackSpaceCloudFiles_Popup_View_Regions.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_cdn_rackspace_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'cdn_rackspace_regions_done' );
11
+ Util_Ui::hidden( '', 'user_name', $details['user_name'] );
12
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
13
+ Util_Ui::hidden( '', 'access_token', $details['access_token'] );
14
+ Util_Ui::hidden( '', 'region_descriptors', $details['region_descriptors_serialized'] );
15
+ echo Util_Ui::nonce_field( 'w3tc' );
16
+
17
+ ?>
18
+ <?php
19
+ if ( isset( $details['error_message'] ) )
20
+ echo '<div class="error">' . $details['error_message'] . '</div>';
21
+ ?>
22
+ <div class="metabox-holder">
23
+ <?php Util_Ui::postbox_header( __( 'Select region', 'w3-total-cache' ) ); ?>
24
+ <table class="form-table">
25
+ <tr>
26
+ <td>Region:</td>
27
+ <td>
28
+ <?php foreach ( $details['region_descriptors'] as $region => $region_details ): ?>
29
+ <label>
30
+ <input name="region" type="radio" class="w3tc-ignore-change"
31
+ value="<?php echo $region ?>" />
32
+ <?php echo $region_details['name'] ?>
33
+ </label><br />
34
+ <?php endforeach ?>
35
+ </tr>
36
+ </table>
37
+
38
+ <p class="submit">
39
+ <input type="button"
40
+ class="w3tc_popup_submit w3tc-button-save button-primary"
41
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
42
+ </p>
43
+ <?php Util_Ui::postbox_footer(); ?>
44
+ </div>
45
+ </form>
lib/CF/cacert.pem → Cdn_RackSpace_Api_CaCert.pem RENAMED
File without changes
Cdn_RackSpace_Api_Cdn.php ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpace_Api_Cdn {
7
+ private $_access_token;
8
+ private $_access_region_descriptor;
9
+ private $_new_access_required = null;
10
+
11
+
12
+
13
+ public function __construct( $config = array() ) {
14
+ $this->_access_token = $config['access_token'];
15
+ $this->_access_region_descriptor = $config['access_region_descriptor'];
16
+ $this->_new_access_required = $config['new_access_required'];
17
+ }
18
+
19
+
20
+
21
+ public function services() {
22
+ $r = $this->_wp_remote_get( '/services' );
23
+ if ( !isset( $r['services'] ) )
24
+ return array();
25
+
26
+ return $r['services'];
27
+ }
28
+
29
+
30
+
31
+ public function service_get( $service ) {
32
+ $response = $this->_wp_remote_get( '/services/' . $service );
33
+
34
+ // expand links to links_by_rel
35
+ if ( isset( $response['links'] ) ) {
36
+ $by_rel = array();
37
+ foreach ( $response['links'] as $r ) {
38
+ $by_rel[ $r['rel'] ] = $r;
39
+ }
40
+ $response['links_by_rel'] = $by_rel;
41
+ }
42
+
43
+ return $response;
44
+ }
45
+
46
+
47
+
48
+ public function service_create( $data ) {
49
+ // required static
50
+ $data['flavor_id'] = 'cdn';
51
+
52
+ return $this->_wp_remote_post( '/services', json_encode( $data ),
53
+ array(
54
+ 'Accept' => 'application/json',
55
+ 'Content-type' => 'application/json'
56
+ ) );
57
+ }
58
+
59
+
60
+
61
+ public function service_set( $service_id, $data ) {
62
+ return $this->_wp_remote_patch( '/services/' . $service_id,
63
+ json_encode( $data ),
64
+ array(
65
+ 'Accept' => 'application/json',
66
+ 'Content-type' => 'application/json'
67
+ ) );
68
+ }
69
+
70
+
71
+
72
+ public function purge( $service_id, $url ) {
73
+ return $this->_wp_remote_delete( '/services/' . $service_id . '/assets?url=' .
74
+ urlencode( $url ) );
75
+ }
76
+
77
+
78
+
79
+ private function _wp_remote_get( $uri ) {
80
+ if ( !empty( $this->_access_region_descriptor['cdn.publicURL'] ) ) {
81
+ $url_base = $this->_access_region_descriptor['cdn.publicURL'];
82
+
83
+ $result = wp_remote_get( $url_base . $uri . '?format=json', array(
84
+ 'headers' => 'X-Auth-Token: ' . $this->_access_token,
85
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem'
86
+ ) );
87
+
88
+ $r = self::_decode_response_json( $result );
89
+ if ( !$r['auth_required'] )
90
+ return $r['response_json'];
91
+ }
92
+
93
+ $new_object = call_user_func( $this->_new_access_required );
94
+ return $new_object->_wp_remote_get( $uri );
95
+ }
96
+
97
+
98
+
99
+ private function _wp_remote_patch( $uri, $body = array(), $headers = array() ) {
100
+ if ( !empty( $this->_access_region_descriptor['cdn.publicURL'] ) ) {
101
+ $url_base = $this->_access_region_descriptor['cdn.publicURL'];
102
+ $headers['X-Auth-Token'] = $this->_access_token;
103
+
104
+ $result = wp_remote_post( $url_base . $uri, array(
105
+ 'headers' => $headers,
106
+ 'body' => $body,
107
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem',
108
+ 'method' => 'PATCH'
109
+ ) );
110
+
111
+ $r = self::_decode_response( $result );
112
+ if ( !$r['auth_required'] ) {
113
+ $location = explode( '/', $result['headers']['location'] );
114
+
115
+ return $location[ count( $location ) - 1 ];
116
+ }
117
+ }
118
+
119
+ $new_object = call_user_func( $this->_new_access_required );
120
+ return $new_object->_wp_remote_patch( $uri, $body );
121
+ }
122
+
123
+
124
+
125
+ private function _wp_remote_post( $uri, $body = array(), $headers = array() ) {
126
+ if ( !empty( $this->_access_region_descriptor['cdn.publicURL'] ) ) {
127
+ $url_base = $this->_access_region_descriptor['cdn.publicURL'];
128
+ $headers['X-Auth-Token'] = $this->_access_token;
129
+
130
+ $result = wp_remote_post( $url_base . $uri, array(
131
+ 'headers' => $headers,
132
+ 'body' => $body,
133
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem'
134
+ ) );
135
+
136
+ $r = self::_decode_response( $result );
137
+ if ( !$r['auth_required'] ) {
138
+ $location = explode( '/', $result['headers']['location'] );
139
+
140
+ return $location[ count( $location ) - 1 ];
141
+ }
142
+ }
143
+
144
+ $new_object = call_user_func( $this->_new_access_required );
145
+ return $new_object->_wp_remote_post( $uri, $body );
146
+ }
147
+
148
+
149
+
150
+ private function _wp_remote_delete( $uri, $headers = array() ) {
151
+ if ( !empty( $this->_access_region_descriptor['cdn.publicURL'] ) ) {
152
+ $url_base = $this->_access_region_descriptor['cdn.publicURL'];
153
+ $headers['X-Auth-Token'] = $this->_access_token;
154
+
155
+ $result = wp_remote_post( $url_base . $uri, array(
156
+ 'headers' => $headers,
157
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem',
158
+ 'method' => 'DELETE'
159
+ ) );
160
+
161
+ $r = self::_decode_response( $result );
162
+ if ( !$r['auth_required'] )
163
+ return;
164
+ }
165
+
166
+ $new_object = call_user_func( $this->_new_access_required );
167
+ return $new_object->_wp_remote_delete( $uri, $body );
168
+ }
169
+
170
+
171
+
172
+ static private function _decode_response_json( $result ) {
173
+ if ( is_wp_error( $result ) )
174
+ throw new \Exception( 'Failed to reach API endpoint' );
175
+
176
+ if ( empty( $result['body'] ) )
177
+ $response_json = array();
178
+ else {
179
+ $response_json = @json_decode( $result['body'], true );
180
+ if ( is_null( $response_json ) )
181
+ throw new \Exception(
182
+ 'Failed to reach API endpoint, got unexpected response ' .
183
+ $result['body'] );
184
+ }
185
+
186
+ if ( $result['response']['code'] != '200' &&
187
+ $result['response']['code'] != '201' &&
188
+ $result['response']['code'] != '202' &&
189
+ $result['response']['code'] != '204' )
190
+ throw new \Exception( $result['body'] );
191
+
192
+ return array( 'response_json' => $response_json, 'auth_required' => false );
193
+ }
194
+
195
+
196
+
197
+ static private function _decode_response( $result ) {
198
+ if ( is_wp_error( $result ) )
199
+ throw new \Exception( 'Failed to reach API endpoint' );
200
+
201
+ if ( $result['response']['code'] != '200' &&
202
+ $result['response']['code'] != '201' &&
203
+ $result['response']['code'] != '202' &&
204
+ $result['response']['code'] != '204' ) {
205
+ // try to decode response
206
+ $response_json = @json_decode( $result['body'], true );
207
+ if ( is_null( $response_json ) ||
208
+ !isset( $response_json['message'] ) )
209
+ throw new \Exception(
210
+ 'Failed to reach API endpoint, got unexpected response ' .
211
+ $result['response']['message'] );
212
+ else {
213
+ $errors = array();
214
+ if ( is_string( $response_json['message'] ) ) {
215
+ $errors[] = $response_json['message'];
216
+ } elseif ( isset( $response_json['message']['errors'] ) ) {
217
+ foreach ( $response_json['message']['errors'] as $error )
218
+ $errors[] = $error['message'];
219
+ }
220
+
221
+ throw new \Exception( implode( ';', $errors ) );
222
+ }
223
+ }
224
+
225
+ return array( 'auth_required' => false );
226
+ }
227
+ }
Cdn_RackSpace_Api_CloudFiles.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpace_Api_CloudFiles {
7
+ private $_access_token;
8
+ private $_access_region_descriptor;
9
+ private $_new_access_required;
10
+
11
+
12
+
13
+ public function __construct( $config = array() ) {
14
+ $this->_access_token = $config['access_token'];
15
+ $this->_access_region_descriptor = $config['access_region_descriptor'];
16
+
17
+ $this->_new_access_required = $config['new_access_required'];
18
+ }
19
+
20
+
21
+
22
+ public function container_create( $container ) {
23
+ return $this->_wp_remote_put( '/' . $container );
24
+ }
25
+
26
+
27
+
28
+ /**
29
+ * data is:
30
+ * name
31
+ * content_type
32
+ * content
33
+ */
34
+ public function object_create( $data ) {
35
+ $headers = array(
36
+ 'ETag' => md5( $data['content'] )
37
+ );
38
+ if ( isset( $data['content_type'] ) )
39
+ $headers['Content-Type'] = $data['content_type'];
40
+
41
+ return $this->_wp_remote_put( '/' . $data['container'] . '/' .
42
+ ltrim( $data['name'], '/' ),
43
+ $data['content'],
44
+ $headers );
45
+ }
46
+
47
+
48
+
49
+ public function object_get_meta_or_null( $container, $name ) {
50
+ return $this->_wp_remote_head( '/' . $container . '/' .
51
+ ltrim( $name, '/' ) );
52
+ }
53
+
54
+
55
+
56
+ public function object_delete( $container, $name ) {
57
+ return $this->_wp_remote_delete( '/' . $container . '/' .
58
+ ltrim( $name, '/' ) );
59
+ }
60
+
61
+
62
+
63
+ private function _wp_remote_put( $uri, $body = array(), $headers = array() ) {
64
+ if ( !empty( $this->_access_region_descriptor['object-store.publicURL'] ) ) {
65
+ $url_base = $this->_access_region_descriptor['object-store.publicURL'];
66
+ $headers['X-Auth-Token'] = $this->_access_token;
67
+ $headers['Accept'] = 'application/json';
68
+
69
+ $result = wp_remote_post( $url_base . $uri . '?format=json', array(
70
+ 'headers' => $headers,
71
+ 'body' => $body,
72
+ 'sslcertificates' => dirname( __FILE__ ) .
73
+ '/Cdn_RackSpace_Api_CaCert.pem',
74
+ 'timeout' => 120,
75
+ 'method' => 'PUT'
76
+ ) );
77
+
78
+ $r = self::_decode_response( $result );
79
+ if ( !$r['auth_required'] )
80
+ return;
81
+ }
82
+
83
+ $new_object = call_user_func( $this->_new_access_required );
84
+ return $new_object->_wp_remote_put( $uri, $body, $headers );
85
+ }
86
+
87
+
88
+
89
+ private function _wp_remote_head( $uri ) {
90
+ if ( !empty( $this->_access_region_descriptor['object-store.publicURL'] ) ) {
91
+ $url_base = $this->_access_region_descriptor['object-store.publicURL'];
92
+
93
+ $result = wp_remote_get( $url_base . $uri . '?format=json', array(
94
+ 'headers' => array( 'X-Auth-Token' => $this->_access_token ),
95
+ 'sslcertificates' => dirname( __FILE__ ) .
96
+ '/Cdn_RackSpace_Api_CaCert.pem',
97
+ 'method' => 'HEAD'
98
+ ) );
99
+
100
+ if ( $result['response']['code'] == '404' )
101
+ return null;
102
+
103
+ $r = self::_decode_response( $result );
104
+ if ( !$r['auth_required'] )
105
+ return $result['headers'];
106
+ }
107
+
108
+ $new_object = call_user_func( $this->_new_access_required );
109
+ return $new_object->_wp_remote_head( $uri );
110
+ }
111
+
112
+
113
+
114
+ private function _wp_remote_delete( $uri ) {
115
+ if ( !empty( $this->_access_region_descriptor['object-store.publicURL'] ) ) {
116
+ $url_base = $this->_access_region_descriptor['object-store.publicURL'];
117
+
118
+ $result = wp_remote_post( $url_base . $uri . '?format=json', array(
119
+ 'headers' => array( 'X-Auth-Token' => $this->_access_token ),
120
+ 'sslcertificates' => dirname( __FILE__ ) .
121
+ '/Cdn_RackSpace_Api_CaCert.pem',
122
+ 'method' => 'DELETE'
123
+ ) );
124
+
125
+ $r = self::_decode_response( $result );
126
+ if ( !$r['auth_required'] )
127
+ return;
128
+ }
129
+
130
+ $new_object = call_user_func( $this->_new_access_required );
131
+ return $new_object->_wp_remote_delete( $uri );
132
+ }
133
+
134
+
135
+
136
+ static private function _decode_response( $result ) {
137
+ if ( is_wp_error( $result ) )
138
+ throw new \Exception( 'Failed to reach API endpoint' );
139
+
140
+ if ( $result['response']['code'] != '200' &&
141
+ $result['response']['code'] != '201' &&
142
+ $result['response']['code'] != '202' &&
143
+ $result['response']['code'] != '204' )
144
+ throw new \Exception(
145
+ 'Failed to reach API endpoint, got unexpected response ' .
146
+ $result['response']['message'] );
147
+
148
+ return array( 'auth_required' => false );
149
+ }
150
+ }
Cdn_RackSpace_Api_CloudFilesCdn.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpace_Api_CloudFilesCdn {
7
+ private $_access_token;
8
+ private $_access_region_descriptor;
9
+ private $_new_access_required = null;
10
+
11
+
12
+
13
+ public function __construct( $config = array() ) {
14
+ $this->_access_token = $config['access_token'];
15
+ $this->_access_region_descriptor = $config['access_region_descriptor'];
16
+ $this->_new_access_required = $config['new_access_required'];
17
+ }
18
+
19
+
20
+
21
+ public function containers() {
22
+ return $this->_wp_remote_get( '' );
23
+ }
24
+
25
+
26
+
27
+ public function container_get( $container ) {
28
+ return $this->_wp_remote_head( '/' . $container );
29
+ }
30
+
31
+
32
+
33
+ public function container_cdn_enable( $container ) {
34
+ return $this->_wp_remote_put( '/' . $container,
35
+ array( 'X-Cdn-Enabled' => 'True' ) );
36
+ }
37
+
38
+
39
+
40
+ private function _wp_remote_get( $uri ) {
41
+ if ( !empty( $this->_access_region_descriptor['object-cdn.publicURL'] ) ) {
42
+ $url_base = $this->_access_region_descriptor['object-cdn.publicURL'];
43
+
44
+ $result = wp_remote_get( $url_base . $uri . '?format=json', array(
45
+ 'headers' => 'X-Auth-Token: ' . $this->_access_token,
46
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem'
47
+ ) );
48
+
49
+ $r = self::_decode_response_json( $result );
50
+ if ( !$r['auth_required'] )
51
+ return $r['response_json'];
52
+ }
53
+
54
+ $new_object = call_user_func( $this->_new_access_required );
55
+ return $new_object->_wp_remote_get( $uri );
56
+ }
57
+
58
+
59
+
60
+ private function _wp_remote_head( $uri, $method = 'GET' ) {
61
+ if ( !empty( $this->_access_region_descriptor['object-cdn.publicURL'] ) ) {
62
+ $url_base = $this->_access_region_descriptor['object-cdn.publicURL'];
63
+
64
+ $result = wp_remote_get( $url_base . $uri . '?format=json', array(
65
+ 'headers' => 'X-Auth-Token: ' . $this->_access_token,
66
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem',
67
+ 'method' => 'HEAD'
68
+ ) );
69
+
70
+ $r = self::_decode_response( $result );
71
+ if ( !$r['auth_required'] )
72
+ return $result['headers'];
73
+ }
74
+
75
+ $new_object = call_user_func( $this->_new_access_required );
76
+ return $new_object->_wp_remote_head( $uri, $body );
77
+ }
78
+
79
+
80
+
81
+ private function _wp_remote_put( $uri, $body = array(), $headers = array() ) {
82
+ if ( !empty( $this->_access_region_descriptor['object-cdn.publicURL'] ) ) {
83
+ $url_base = $this->_access_region_descriptor['object-cdn.publicURL'];
84
+ $headers['X-Auth-Token'] = $this->_access_token;
85
+
86
+ $result = wp_remote_post( $url_base . $uri, array(
87
+ 'headers' => $headers,
88
+ 'body' => $body,
89
+ 'sslcertificates' => dirname( __FILE__ ) . '/Cdn_RackSpace_Api_CaCert.pem',
90
+ 'method' => 'PUT'
91
+ ) );
92
+
93
+ $r = self::_decode_response( $result );
94
+ if ( !$r['auth_required'] )
95
+ return;
96
+ }
97
+
98
+ $new_object = call_user_func( $this->_new_access_required );
99
+ return $new_object->_wp_remote_put( $uri, $body, $headers );
100
+ }
101
+
102
+
103
+
104
+ static private function _decode_response_json( $result ) {
105
+ if ( is_wp_error( $result ) )
106
+ throw new \Exception( 'Failed to reach API endpoint' );
107
+
108
+ if ( empty( $result['body'] ) )
109
+ $response_json = array();
110
+ else {
111
+ $response_json = @json_decode( $result['body'], true );
112
+ if ( is_null( $response_json ) )
113
+ throw new \Exception(
114
+ 'Failed to reach API endpoint, got unexpected response ' .
115
+ $result['body'] );
116
+ }
117
+
118
+ if ( $result['response']['code'] != '200' &&
119
+ $result['response']['code'] != '201' &&
120
+ $result['response']['code'] != '202' &&
121
+ $result['response']['code'] != '204' )
122
+ throw new \Exception( $result['body'] );
123
+
124
+ return array( 'response_json' => $response_json, 'auth_required' => false );
125
+ }
126
+
127
+
128
+
129
+ static private function _decode_response( $result ) {
130
+ if ( is_wp_error( $result ) )
131
+ throw new \Exception( 'Failed to reach API endpoint' );
132
+
133
+ if ( $result['response']['code'] != '200' &&
134
+ $result['response']['code'] != '201' &&
135
+ $result['response']['code'] != '202' &&
136
+ $result['response']['code'] != '204' )
137
+ throw new \Exception(
138
+ 'Failed to reach API endpoint, got unexpected response ' .
139
+ $result['response']['message'] );
140
+
141
+ return array( 'auth_required' => false );
142
+ }
143
+ }
Cdn_RackSpace_Api_Tokens.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Cdn_RackSpace_Api_Tokens {
7
+ static public function authenticate( $user_name, $api_key ) {
8
+ $request_json = array( 'auth' =>
9
+ array( 'RAX-KSKEY:apiKeyCredentials' => array(
10
+ 'username' => $user_name,
11
+ 'apiKey' => $api_key
12
+ ) ) );
13
+
14
+ $result = wp_remote_post(
15
+ 'https://identity.api.rackspacecloud.com/v2.0/tokens',
16
+ array(
17
+ 'headers' => array(
18
+ 'Accept' => 'application/json',
19
+ 'Content-Type' => 'application/json'
20
+ ),
21
+ 'sslcertificates' => dirname( __FILE__ ) .
22
+ '/Cdn_RackSpace_Api_CaCert.pem',
23
+ 'body' => json_encode( $request_json )
24
+ )
25
+ );
26
+
27
+ $response = self::_decode_response( $result );
28
+ if ( !isset( $response['access'] ) )
29
+ throw new \Exception(
30
+ 'Unexpected authentication response: access token not found' );
31
+
32
+ $r = $response['access'];
33
+
34
+ // fill service descriptors by region
35
+
36
+ if ( !isset( $r['serviceCatalog'] ) )
37
+ throw new \Exception(
38
+ 'Unexpected authentication response: serviceCatalog token not found' );
39
+ $services = $r['serviceCatalog'];
40
+
41
+ return array(
42
+ 'access_token' => $r['token']['id'],
43
+ 'services' => $services
44
+ );
45
+ }
46
+
47
+
48
+
49
+ static public function cloudfiles_services_by_region( $services ) {
50
+ $by_region = array();
51
+
52
+ foreach ( $services as $s ) {
53
+ if ( $s['type'] == 'object-store' ) {
54
+ foreach ( $s['endpoints'] as $endpoint ) {
55
+ $region = $endpoint['region'];
56
+ if ( !isset( $by_region[$region] ) )
57
+ $by_region[$region] = array();
58
+
59
+ $by_region[$region]['object-store.publicURL'] =
60
+ $endpoint["publicURL"];
61
+ $by_region[$region]['object-store.internalURL'] =
62
+ $endpoint["internalURL"];
63
+ }
64
+ } elseif ( $s['type'] == 'rax:object-cdn' ) {
65
+ foreach ( $s['endpoints'] as $endpoint ) {
66
+ $region = $endpoint['region'];
67
+ if ( !isset( $by_region[$region] ) )
68
+ $by_region[$region] = array();
69
+
70
+ $by_region[$region]['object-cdn.publicURL'] =
71
+ $endpoint["publicURL"];
72
+ }
73
+ }
74
+ }
75
+
76
+ $by_region = self::_add_region_names( $by_region );
77
+ return $by_region;
78
+ }
79
+
80
+
81
+
82
+ static public function cdn_services_by_region( $services ) {
83
+ $by_region = array();
84
+
85
+ foreach ( $services as $s ) {
86
+ if ( $s['type'] == 'rax:cdn' ) {
87
+ foreach ( $s['endpoints'] as $endpoint ) {
88
+ $region = $endpoint['region'];
89
+ if ( !isset( $by_region[$region] ) )
90
+ $by_region[$region] = array();
91
+
92
+ $by_region[$region]['cdn.publicURL'] =
93
+ $endpoint["publicURL"];
94
+ }
95
+ }
96
+ }
97
+
98
+ $by_region = self::_add_region_names( $by_region );
99
+ return $by_region;
100
+ }
101
+
102
+
103
+
104
+ static private function _add_region_names( $by_region ) {
105
+ // try to decode region names
106
+ $region_names = array(
107
+ 'ORD' => 'Chicago (ORD)',
108
+ 'DFW' => 'Dallas/Ft. Worth (DFW)',
109
+ 'HKG' => 'Hong Kong (HKG)',
110
+ 'LON' => 'London (LON)',
111
+ 'IAD' => 'Northern Virginia (IAD)',
112
+ 'SYD' => 'Sydney (SYD)'
113
+ );
114
+
115
+ $keys = array_keys( $by_region );
116
+ foreach ( $keys as $region ) {
117
+ if ( isset( $region_names[$region] ) )
118
+ $by_region[$region]['name'] = $region_names[$region];
119
+ else
120
+ $by_region[$region]['name'] = $region;
121
+ }
122
+
123
+ return $by_region;
124
+ }
125
+
126
+
127
+
128
+ static private function _decode_response( $result ) {
129
+ if ( is_wp_error( $result ) )
130
+ throw new \Exception( 'Failed to reach API endpoint' );
131
+
132
+ $response_json = @json_decode( $result['body'], true );
133
+ if ( is_null( $response_json ) )
134
+ throw new \Exception(
135
+ 'Failed to reach API endpoint, got unexpected response ' .
136
+ $result['body'] );
137
+ if ( isset( $response_json['unauthorized']['message'] ) )
138
+ throw new \Exception( $response_json['unauthorized']['message'] );
139
+
140
+ if ( $result['response']['code'] != '200' && $result['response']['code'] != '201' )
141
+ throw new \Exception( $result['body'] );
142
+
143
+ return $response_json;
144
+ }
145
+ }
Cdn_Util.php ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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',
16
+ 'cf',
17
+ 'cloudfront_fsd',
18
+ 'cf2',
19
+ 'cotendo',
20
+ 'edgecast',
21
+ 'maxcdn_fsd',
22
+ 'ftp',
23
+ 'google_drive',
24
+ 'highwinds',
25
+ 'maxcdn',
26
+ 'mirror',
27
+ 'netdna',
28
+ 'rscf',
29
+ 'rackspace_cdn',
30
+ 's3',
31
+ 's3_compatible',
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', 'netdna', 'maxcdn', 'cotendo', 'cf2', 'akamai',
44
+ 'edgecast', 'att', 'highwinds', 'rackspace_cdn' ) );
45
+ }
46
+
47
+ /**
48
+ * Returns true if CDN engine is mirror
49
+ *
50
+ * @param string $engine
51
+ * @return bool
52
+ */
53
+ static public function is_engine_fsd( $engine ) {
54
+ return in_array( $engine, array(
55
+ 'cloudfront_fsd',
56
+ 'maxcdn_fsd'
57
+ ) );
58
+ }
59
+
60
+ static public function is_engine_push( $engine ) {
61
+ return !self::is_engine_mirror( $engine ) && !self::is_engine_fsd( $engine );
62
+ }
63
+
64
+ /**
65
+ * Returns true if CDN has purge all support
66
+ *
67
+ * @param unknown $engine
68
+ * @return bool
69
+ */
70
+ static public function can_purge_all( $engine ) {
71
+ return in_array( $engine, array(
72
+ 'att',
73
+ 'cotendo',
74
+ 'edgecast',
75
+ 'maxcdn_fsd',
76
+ 'highwinds',
77
+ 'maxcdn',
78
+ 'netdna',
79
+ ) );
80
+ }
81
+
82
+ /**
83
+ * Returns true if CDN engine is supporting purge
84
+ *
85
+ * @param string $engine
86
+ * @return bool
87
+ */
88
+ static public function can_purge( $engine ) {
89
+ return in_array( $engine, array(
90
+ 'akamai',
91
+ 'att',
92
+ 'azure',
93
+ 'cf',
94
+ 'cf2',
95
+ 'cloudfront_fsd',
96
+ 'cotendo',
97
+ 'edgecast',
98
+ 'maxcdn_fsd',
99
+ 'ftp',
100
+ 'highwinds',
101
+ 'maxcdn',
102
+ 'netdna',
103
+ 'rscf',
104
+ 's3',
105
+ 's3_compatible',
106
+ ) );
107
+ }
108
+
109
+ /**
110
+ * Returns true if CDN supports realtime purge. That is purging on post changes, comments etc.
111
+ *
112
+ * @param unknown $engine
113
+ * @return bool
114
+ */
115
+ static public function supports_realtime_purge( $engine ) {
116
+ return !in_array( $engine, array( 'cf2' ) );
117
+ }
118
+
119
+ /**
120
+ * Search files
121
+ *
122
+ * @param string $search_dir
123
+ * @param string $base_dir
124
+ * @param string $mask
125
+ * @param boolean $recursive
126
+ * @return array
127
+ */
128
+ static function search_files( $search_dir, $base_dir, $mask = '*.*', $recursive = true ) {
129
+ static $stack = array();
130
+ $files = array();
131
+ $ignore = array(
132
+ '.svn',
133
+ '.git',
134
+ '.DS_Store',
135
+ 'CVS',
136
+ 'Thumbs.db',
137
+ 'desktop.ini'
138
+ );
139
+
140
+ $dir = @opendir( $search_dir );
141
+
142
+ if ( $dir ) {
143
+ while ( ( $entry = @readdir( $dir ) ) !== false ) {
144
+ if ( $entry != '.' && $entry != '..' && !in_array( $entry, $ignore ) ) {
145
+ $path = $search_dir . '/' . $entry;
146
+
147
+ if ( @is_dir( $path ) && $recursive ) {
148
+ array_push( $stack, $entry );
149
+ $files = array_merge( $files, self::search_files(
150
+ $path, $base_dir, $mask, $recursive ) );
151
+ array_pop( $stack );
152
+ } else {
153
+ $regexp = '~^(' . self::get_regexp_by_mask( $mask ) . ')$~i';
154
+
155
+ if ( preg_match( $regexp, $entry ) ) {
156
+ $tmp = $base_dir != '' ? $base_dir . '/' : '';
157
+ $tmp .= ( $p = implode( '/', $stack ) ) != '' ? $p . '/' : '';
158
+ $files[] = $tmp . $entry;
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ @closedir( $dir );
165
+ }
166
+
167
+ return $files;
168
+ }
169
+
170
+ /**
171
+ * Returns regexp by mask
172
+ *
173
+ * @param string $mask
174
+ * @return string
175
+ */
176
+ static function get_regexp_by_mask( $mask ) {
177
+ $mask = trim( $mask );
178
+ $mask = Util_Environment::preg_quote( $mask );
179
+
180
+ $mask = str_replace( array(
181
+ '\*',
182
+ '\?',
183
+ ';'
184
+ ), array(
185
+ '@ASTERISK@',
186
+ '@QUESTION@',
187
+ '|'
188
+ ), $mask );
189
+
190
+ $regexp = str_replace( array(
191
+ '@ASTERISK@',
192
+ '@QUESTION@'
193
+ ), array(
194
+ '[^\\?\\*:\\|\'"<>]*',
195
+ '[^\\?\\*:\\|\'"<>]'
196
+ ), $mask );
197
+
198
+ return $regexp;
199
+ }
200
+
201
+ static function replace_folder_placeholders( $file ) {
202
+ static $content_dir, $plugin_dir, $upload_dir;
203
+ if ( empty( $content_dir ) ) {
204
+ $content_dir = str_replace( Util_Environment::document_root(), '', WP_CONTENT_DIR );
205
+ $content_dir = substr( $content_dir, strlen( Util_Environment::site_url_uri() ) );
206
+ $content_dir = trim( $content_dir, '/' );
207
+ if ( defined( 'WP_PLUGIN_DIR' ) ) {
208
+ $plugin_dir = str_replace( Util_Environment::document_root(), '', WP_PLUGIN_DIR );
209
+ $plugin_dir = trim( $plugin_dir, '/' );
210
+ } else {
211
+ $plugin_dir = str_replace( Util_Environment::document_root(), '', WP_CONTENT_DIR . '/plugins' );
212
+ $plugin_dir = trim( $plugin_dir, '/' );
213
+ }
214
+ $upload_dir = Util_Environment::wp_upload_dir();
215
+ $upload_dir = str_replace( Util_Environment::document_root(), '', $upload_dir['basedir'] );
216
+ $upload_dir = trim( $upload_dir, '/' );
217
+ }
218
+ $file = str_replace( '{wp_content_dir}', $content_dir, $file );
219
+ $file = str_replace( '{plugins_dir}', $plugin_dir, $file );
220
+ $file = str_replace( '{uploads_dir}', $upload_dir, $file );
221
+
222
+ return $file;
223
+ }
224
+ }
Cli.php ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * The W3 Total Cache plugin
6
+ *
7
+ * @package wp-cli
8
+ * @subpackage commands/third-party
9
+ */
10
+ class W3TotalCache_Command extends \WP_CLI_Command {
11
+
12
+ /**
13
+ * Clear something from the cache
14
+ *
15
+ * @param array $args
16
+ * @param array $vars
17
+ */
18
+ function flush( $args = array(), $vars = array() ) {
19
+ $args = array_unique( $args );
20
+
21
+ do {
22
+ $cache_type = array_shift( $args );
23
+
24
+ switch ( $cache_type ) {
25
+ case 'db':
26
+ case 'database':
27
+ try {
28
+ $w3_db = Dispatcher::component( 'CacheFlush' );
29
+ $w3_db->dbcache_flush();
30
+ }
31
+ catch ( \Exception $e ) {
32
+ \WP_CLI::error( __( 'Flushing the DB cache failed.', 'w3-total-cache' ) );
33
+ }
34
+ \WP_CLI::success( __( 'The DB cache is flushed successfully.', 'w3-total-cache' ) );
35
+ break;
36
+
37
+ case 'minify':
38
+ try {
39
+ $w3_minify = Dispatcher::component( 'CacheFlush' );
40
+ $w3_minify->minifycache_flush();
41
+ }
42
+ catch ( \Exception $e ) {
43
+ \WP_CLI::error( __( 'Flushing the minify cache failed.', 'w3-total-cache' ) );
44
+ }
45
+ \WP_CLI::success( __( 'The minify cache is flushed successfully.', 'w3-total-cache' ) );
46
+ break;
47
+
48
+ case 'object':
49
+ try {
50
+ $w3_objectcache = Dispatcher::component( 'CacheFlush' );
51
+ $w3_objectcache->objectcache_flush();
52
+ }
53
+ catch ( \Exception $e ) {
54
+ \WP_CLI::error( __( 'Flushing the object cache failed.', 'w3-total-cache' ) );
55
+ }
56
+ \WP_CLI::success( __( 'The object cache is flushed successfully.', 'w3-total-cache' ) );
57
+ break;
58
+
59
+ case 'post':
60
+ default:
61
+ if ( isset( $vars['post_id'] ) ) {
62
+ if ( is_numeric( $vars['post_id'] ) ) {
63
+ try {
64
+ $w3_cacheflush = Dispatcher::component( 'CacheFlush' );
65
+ $w3_cacheflush->flush_post( $vars['post_id'] );
66
+ }
67
+ catch ( \Exception $e ) {
68
+ \WP_CLI::error( __( 'Flushing the page from cache failed.', 'w3-total-cache' ) );
69
+ }
70
+ \WP_CLI::success( __( 'The page is flushed from cache successfully.', 'w3-total-cache' ) );
71
+ } else {
72
+ \WP_CLI::error( __( 'This is not a valid post id.', 'w3-total-cache' ) );
73
+ }
74
+
75
+ w3tc_flush_post( $vars['post_id'] );
76
+ }
77
+ elseif ( isset( $vars['permalink'] ) ) {
78
+ $id = url_to_postid( $vars['permalink'] );
79
+
80
+ if ( is_numeric( $id ) ) {
81
+ try {
82
+ $w3_cacheflush = Dispatcher::component( 'CacheFlush' );
83
+ $w3_cacheflush->flush_post( $id );
84
+ }
85
+ catch ( \Exception $e ) {
86
+ \WP_CLI::error( __( 'Flushing the page from cache failed.', 'w3-total-cache' ) );
87
+ }
88
+ \WP_CLI::success( __( 'The page is flushed from cache successfully.', 'w3-total-cache' ) );
89
+ } else {
90
+ \WP_CLI::error( __( 'There is no post with this permalink.', 'w3-total-cache' ) );
91
+ }
92
+ } else {
93
+ if ( isset( $flushed_page_cache ) && $flushed_page_cache )
94
+ break;
95
+
96
+ $flushed_page_cache = true;
97
+ try {
98
+ $w3_cacheflush = Dispatcher::component( 'CacheFlush' );
99
+ $w3_cacheflush->flush_posts();
100
+ }
101
+ catch ( \Exception $e ) {
102
+ \WP_CLI::error( __( 'Flushing the page cache failed.', 'w3-total-cache' ) );
103
+ }
104
+ \WP_CLI::success( __( 'The page cache is flushed successfully.', 'w3-total-cache' ) );
105
+ }
106
+ }
107
+ } while ( !empty( $args ) );
108
+ }
109
+
110
+
111
+ /**
112
+ * Update query string function
113
+ */
114
+ function querystring() {
115
+
116
+ try {
117
+ $w3_querystring = Dispatcher::component( 'CacheFlush' );
118
+ $w3_querystring->browsercache_flush();
119
+ }
120
+ catch ( \Exception $e ) {
121
+ \WP_CLI::error( sprintf(
122
+ __( 'updating the query string failed. with error %s', 'w3-total-cache' ),
123
+ $e ) );
124
+ }
125
+
126
+ \WP_CLI::success( __( 'The query string was updated successfully.', 'w3-total-cache' ) );
127
+
128
+ }
129
+
130
+ /**
131
+ * Purge URL's from cdn and varnish if enabled
132
+ *
133
+ * @param array $args
134
+ */
135
+ function cdn_purge( $args = array() ) {
136
+ $purgeitems = array();
137
+ foreach ( $args as $file ) {
138
+ $cdncommon = Dispatcher::component( 'Cdn_Core' );
139
+ $local_path = WP_ROOT . $file;
140
+ $remote_path = $file;
141
+ $purgeitems[] = $cdncommon->build_file_descriptor( $local_path, $remote_path );
142
+ }
143
+
144
+ try {
145
+ $w3_cdn_purge = Dispatcher::component( 'CacheFlush' );
146
+ $w3_cdn_purge->cdn_purge_files( $purgeitems );
147
+ }
148
+ catch ( \Exception $e ) {
149
+ \WP_CLI::error( __( 'Files did not successfully purge with error %s', 'w3-total-cache' ), $e );
150
+ }
151
+ \WP_CLI::success( __( 'Files purged successfully.', 'w3-total-cache' ) );
152
+
153
+ }
154
+
155
+ /**
156
+ * Tell opcache to reload PHP files
157
+ *
158
+ * @param array $args
159
+ */
160
+ function opcache_flush_file( $args = array() ) {
161
+ try {
162
+ $method = array_shift( $args );
163
+ if ( !in_array( $method, array( 'SNS', 'local' ) ) )
164
+ \WP_CLI::error( $method . __( ' is not supported. Change to SNS or local to reload opcache files', 'w3-total-cache' ) );
165
+ if ( $method == 'SNS' ) {
166
+ $w3_cache = Dispatcher::component( 'CacheFlush' );
167
+ $w3_cache->opcache_flush_file( $args[0] );
168
+ } else {
169
+ $url = WP_PLUGIN_URL . '/' . dirname( W3TC_FILE ) . '/pub/opcache.php';
170
+ $path = parse_url( $url, PHP_URL_PATH );
171
+ $post = array(
172
+ 'method' => 'POST',
173
+ 'timeout' => 45,
174
+ 'redirection' => 5,
175
+ 'httpversion' => '1.0',
176
+ 'blocking' => true,
177
+ 'body' => array(
178
+ 'nonce' => wp_hash( $path ),
179
+ 'command' => 'flush_file',
180
+ 'file' => $args[0]
181
+ ),
182
+ );
183
+ $result = wp_remote_post( $url, $post );
184
+ if ( is_wp_error( $result ) ) {
185
+ \WP_CLI::error( __( 'Files did not successfully reload with error %s', 'w3-total-cache' ), $result );
186
+ } elseif ( $result['response']['code'] != '200' ) {
187
+ \WP_CLI::error( __( 'Files did not successfully reload with message: ', 'w3-total-cache' ) . $result['body'] );
188
+ }
189
+ }
190
+ }
191
+ catch ( \Exception $e ) {
192
+ \WP_CLI::error( __( 'Files did not successfully reload with error %s', 'w3-total-cache' ), $e );
193
+ }
194
+ \WP_CLI::success( __( 'Files reloaded successfully.', 'w3-total-cache' ) );
195
+
196
+ }
197
+
198
+ /**
199
+ * Tell opcache to reload PHP files
200
+ *
201
+ * @param array $args
202
+ */
203
+ function opcache_flush( $args = array() ) {
204
+ try {
205
+ $method = array_shift( $args );
206
+ if ( !in_array( $method, array( 'SNS', 'local' ) ) )
207
+ \WP_CLI::error( $method . __( ' is not supported. Change to SNS or local to delete opcache files', 'w3-total-cache' ) );
208
+
209
+ if ( $method == 'SNS' ) {
210
+ $w3_cache = Dispatcher::component( 'CacheFlush' );
211
+ $w3_cache->opcache_flush();
212
+ } else {
213
+ $url = WP_PLUGIN_URL . '/' . dirname( W3TC_FILE ) . '/pub/opcache.php';
214
+ $path = parse_url( $url, PHP_URL_PATH );
215
+ $post = array(
216
+ 'method' => 'POST',
217
+ 'timeout' => 45,
218
+ 'redirection' => 5,
219
+ 'httpversion' => '1.0',
220
+ 'blocking' => true,
221
+ 'body' => array(
222
+ 'nonce' => wp_hash( $path ),
223
+ 'command' => 'flush'
224
+ ),
225
+ );
226
+ $result = wp_remote_post( $url, $post );
227
+ if ( is_wp_error( $result ) ) {
228
+ \WP_CLI::error( __( 'Files did not successfully delete with error %s', 'w3-total-cache' ), $result );
229
+ } elseif ( $result['response']['code'] != '200' ) {
230
+ \WP_CLI::error( __( 'Files did not successfully delete with message: ', 'w3-total-cache' ). $result['body'] );
231
+ }
232
+ }
233
+ }
234
+ catch ( \Exception $e ) {
235
+ \WP_CLI::error( __( 'Files did not successfully delete with error %s', 'w3-total-cache' ), $e );
236
+ }
237
+ \WP_CLI::success( __( 'Files deleted successfully.', 'w3-total-cache' ) );
238
+
239
+ }
240
+
241
+ /**
242
+ * triggers PgCache Garbage Cleanup
243
+ */
244
+ function pgcache_cleanup() {
245
+ try {
246
+ $pgcache_cleanup = Dispatcher::component( 'PgCache_Plugin_Admin' );
247
+ $pgcache_cleanup->cleanup();
248
+ } catch ( \Exception $e ) {
249
+ \WP_CLI::error( __( 'PageCache Garbage cleanup did not start with error %s',
250
+ 'w3-total-cache' ), $e );
251
+ }
252
+
253
+ \WP_CLI::success( __( 'PageCache Garbage cleanup triggered successfully.',
254
+ 'w3-total-cache' ) );
255
+ }
256
+
257
+ /**
258
+ * triggers PgCache Garbage Cleanup
259
+ */
260
+ function fix_environment( $args = array(), $vars = array() ) {
261
+ $server_type = array_shift( $args );
262
+ switch ( $server_type ) {
263
+ case 'apache':
264
+ $_SERVER['SERVER_SOFTWARE'] = 'Apache';
265
+ break;
266
+ case 'nginx':
267
+ $_SERVER['SERVER_SOFTWARE'] = 'nginx';
268
+ break;
269
+ }
270
+
271
+ try {
272
+ $config = Dispatcher::config();
273
+ $environment = Dispatcher::component( 'Root_Environment' );
274
+ $environment->fix_in_wpadmin( $config, true );
275
+ } catch ( Util_Environment_Exceptions $e ) {
276
+ \WP_CLI::error( __( 'Environment adjustment failed with error', 'w3-total-cache' ),
277
+ $e->getCombinedMessage() );
278
+ }
279
+
280
+ \WP_CLI::success( __( 'Environment adjusted.', 'w3-total-cache' ) );
281
+ }
282
+
283
+ /**
284
+ * Help function for this command
285
+ */
286
+ public static function help() {
287
+ \WP_CLI::line( <<<EOB
288
+ usage: wp w3-total-cache flush [post|database|minify|object] [--post_id=<post-id>] [--permalink=<post-permalink>]
289
+ or : wp w3-total-cache querystring
290
+ or : wp w3-total-cache cdn_purge <file> [<file2>]...
291
+ or : wp w3-total-cache pgcache_cleanup
292
+
293
+ flush flushes whole cache or specific items based on provided arguments
294
+ querystring update query string for all static files
295
+ cdn_purge Purges command line provided files from Varnish and the CDN
296
+ pgcache_cleanup Generally triggered from a cronjob, allows for manual Garbage collection of page cache to be triggered
297
+ opcache_flush_file SNS/local file.php Tells opcache to compile files
298
+ opcache_flush SNS/local expression Tells opcache to delete all files
299
+ fix_environment Creates missing files, writes apache/nginx rules. Subcommand defines server type:
300
+ apache create rules for apache server
301
+ nginx create rules for nginx server
302
+ Available flush sub-commands:
303
+ --post_id=<id> flush a specific post ID
304
+ --permalink=<post-permalink> flush a specific permalink
305
+ database flush the database cache
306
+ object flush the object cache
307
+ minify flush the minify cache
308
+ EOB
309
+ );
310
+ }
311
+ }
312
+
313
+ if ( method_exists( '\WP_CLI', 'add_command' ) ) {
314
+ \WP_CLI::add_command( 'w3-total-cache', '\W3TC\W3TotalCache_Command' );
315
+ \WP_CLI::add_command( 'total-cache', '\W3TC\W3TotalCache_Command' );
316
+ } else {
317
+ // backward compatibility
318
+ \WP_CLI::addCommand( 'w3-total-cache', '\W3TC\W3TotalCache_Command' );
319
+ \WP_CLI::addCommand( 'total-cache', '\W3TC\W3TotalCache_Command' );
320
+ }
Config.php ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class Config
6
+ * Provides configuration data using cache
7
+ */
8
+ class Config {
9
+ /*
10
+ * blog id of loaded config
11
+ * @var integer
12
+ */
13
+ private $_blog_id;
14
+ private $_is_master;
15
+
16
+ /*
17
+ * Is this preview config
18
+ * @var boolean
19
+ */
20
+ private $_preview;
21
+
22
+ private $_md5;
23
+ private $_data;
24
+
25
+
26
+
27
+ /**
28
+ * Reads config from file and returns it's content as array (or null)
29
+ * Stored in this class to limit class loading
30
+ */
31
+ static public function util_array_from_file( $filename ) {
32
+ if ( file_exists( $filename ) && is_readable( $filename ) ) {
33
+ // including file directly instead of read+eval causes constant
34
+ // problems with APC, ZendCache, and WSOD in a case of
35
+ // broken config file
36
+ $content = @file_get_contents( $filename );
37
+ $config = @json_decode( $content, true );
38
+
39
+ if ( is_array( $config ) )
40
+ return $config;
41
+ }
42
+
43
+ return null;
44
+ }
45
+
46
+
47
+
48
+ /*
49
+ * Returns config filename
50
+ * Stored in this class to limit class loading
51
+ */
52
+ static public function util_config_filename( $blog_id, $preview ) {
53
+ $postfix = ( $preview ? '-preview' : '' ) . '.json';
54
+
55
+ if ( $blog_id <= 0 )
56
+ return W3TC_CONFIG_DIR . '/master' . $postfix;
57
+ else
58
+ return W3TC_CONFIG_DIR . '/' . sprintf( '%06d', $blog_id ) . $postfix;
59
+ }
60
+
61
+
62
+
63
+ /*
64
+ * Returns config filename
65
+ * Stored in this class to limit class loading
66
+ * v<0.9.5
67
+ */
68
+ static public function util_config_filename_legacy( $blog_id, $preview ) {
69
+ $postfix = ( $preview ? '-preview' : '' ) . '.php';
70
+
71
+ if ( $blog_id <= 0 )
72
+ return W3TC_CONFIG_DIR . '/master' . $postfix;
73
+ else
74
+ return W3TC_CONFIG_DIR . '/' . sprintf( '%06d', $blog_id ) . $postfix;
75
+ }
76
+
77
+
78
+
79
+ public function __construct( $blog_id = null ) {
80
+ if ( !is_null( $blog_id ) ) {
81
+ $this->_blog_id = $blog_id;
82
+ $this->_is_master = ( $this->_blog_id == 0 );
83
+ } else {
84
+ if ( Util_Environment::is_using_master_config() )
85
+ $this->_blog_id = 0;
86
+ else
87
+ $this->_blog_id = Util_Environment::blog_id();
88
+
89
+ $this->_is_master = ( Util_Environment::blog_id() == 0 );
90
+ }
91
+
92
+ $this->_preview = Util_Environment::is_preview_mode();
93
+ $this->load();
94
+ }
95
+
96
+
97
+
98
+ /**
99
+ * Returns config value. Implementation for overriding
100
+ */
101
+ public function get( $key, $default = null ) {
102
+ $v = $this->_get( $this->_data, $key );
103
+ if ( !is_null( $v ) )
104
+ return $v;
105
+
106
+ // take default value
107
+ if ( !empty( $default ) || !function_exists( 'apply_filters' ) )
108
+ return $default;
109
+
110
+ // try cached default values
111
+ static $default_values = null;
112
+ if ( is_null( $default_values ) )
113
+ $default_values = apply_filters( 'w3tc_config_default_values',
114
+ array() );
115
+
116
+ $v = $this->_get( $default_values, $key );
117
+ if ( !is_null( $v ) )
118
+ return $v;
119
+
120
+ // update default values
121
+ $default_values = apply_filters( 'w3tc_config_default_values',
122
+ array() );
123
+
124
+ $v = $this->_get( $default_values, $key );
125
+ if ( !is_null( $v ) )
126
+ return $v;
127
+
128
+ return $default;
129
+ }
130
+
131
+
132
+
133
+ private function _get( &$a, $key ) {
134
+ if ( is_array( $key ) ) {
135
+ $key0 = $key[0];
136
+ if ( isset( $a[$key0] ) ) {
137
+ $key1 = $key[1];
138
+ if ( isset( $a[$key0][$key1] ) )
139
+ return $a[$key0][$key1];
140
+ }
141
+ } else if ( isset( $a[$key] ) ) {
142
+ return $a[$key];
143
+ }
144
+
145
+ return null;
146
+ }
147
+
148
+
149
+
150
+ /**
151
+ * Returns string value
152
+ */
153
+ public function get_string( $key, $default = '', $trim = true ) {
154
+ $value = (string)$this->get( $key, $default );
155
+
156
+ return $trim ? trim( $value ) : $value;
157
+ }
158
+
159
+
160
+
161
+ /**
162
+ * Returns integer value
163
+ */
164
+ public function get_integer( $key, $default = 0 ) {
165
+ return (integer)$this->get( $key, $default );
166
+ }
167
+
168
+
169
+
170
+ /**
171
+ * Returns boolean value
172
+ */
173
+ public function get_boolean( $key, $default = false ) {
174
+ return (boolean)$this->get( $key, $default );
175
+ }
176
+
177
+
178
+
179
+ /**
180
+ * Returns array value
181
+ */
182
+ public function get_array( $key, $default = array() ) {
183
+ return (array)$this->get( $key, $default );
184
+ }
185
+
186
+
187
+
188
+ /**
189
+ * Check if an extension is active
190
+ */
191
+ public function is_extension_active( $extension ) {
192
+ $extensions = $this->get_array( 'extensions.active' );
193
+ return isset( $extensions[$extension] );
194
+ }
195
+
196
+
197
+
198
+ public function is_extension_active_frontend( $extension ) {
199
+ $extensions = $this->get_array( 'extensions.active_frontend' );
200
+ return isset( $extensions[$extension] );
201
+ }
202
+
203
+
204
+
205
+ public function set_extension_active_frontend( $extension,
206
+ $is_active_frontend ) {
207
+ $a = $this->get_array( 'extensions.active_frontend' );
208
+ if ( !$is_active_frontend )
209
+ unset( $a[$extension] );
210
+ else
211
+ $a[$extension] = '*';
212
+
213
+ $this->set( 'extensions.active_frontend', $a );
214
+ }
215
+
216
+ /**
217
+ * Sets config value.
218
+ * Method to override
219
+ */
220
+ public function set( $key, $value ) {
221
+ if ( !is_array( $key ) ) {
222
+ $this->_data[$key] = $value;
223
+ } else {
224
+ // set extension's key
225
+ $key0 = $key[0];
226
+ $key1 = $key[1];
227
+
228
+ if ( !isset( $this->_data[$key0] ) || !is_array( $this->_data[$key0] ) )
229
+ $this->_data[$key0] = array();
230
+
231
+ $this->_data[$key0][$key1] = $value;
232
+ }
233
+
234
+ return $value;
235
+ }
236
+
237
+ /**
238
+ * Check if we are in preview mode
239
+ */
240
+ public function is_preview() {
241
+ return $this->_preview;
242
+ }
243
+
244
+ /**
245
+ * Returns true if we edit master config
246
+ */
247
+ public function is_master() {
248
+ return $this->_is_master;
249
+ }
250
+
251
+
252
+
253
+ /**
254
+ * Sets default values
255
+ */
256
+ public function set_defaults() {
257
+ $c = new ConfigCompiler( $this->_blog_id, $this->_preview );
258
+ $this->_data = $c->get_data();
259
+ }
260
+
261
+
262
+
263
+ /**
264
+ * Saves modified config
265
+ */
266
+ public function save() {
267
+ if ( function_exists( 'do_action' ) )
268
+ do_action( 'w3tc_config_save', $this );
269
+
270
+ $c = new ConfigCompiler( $this->_blog_id, $this->_preview );
271
+ $c->apply_data( $this->_data );
272
+ $c->save();
273
+ }
274
+
275
+
276
+
277
+ public function is_sealed( $key ) {
278
+ if ( $this->is_master() )
279
+ return false;
280
+
281
+ // better to use master config data here, but
282
+ // its faster and preciese enough for UI
283
+ return ConfigCompiler::child_key_sealed( $key, $this->_data,
284
+ $this->_data );
285
+ }
286
+
287
+
288
+
289
+ /**
290
+ * Exports config content
291
+ */
292
+ public function export() {
293
+ if ( defined( 'JSON_PRETTY_PRINT' ) )
294
+ $content = json_encode( $this->_data, JSON_PRETTY_PRINT );
295
+ else
296
+ $content = json_encode( $this->_data );
297
+
298
+ return $content;
299
+ }
300
+
301
+
302
+
303
+ /**
304
+ * Imports config content
305
+ */
306
+ public function import( $filename ) {
307
+ if ( file_exists( $filename ) && is_readable( $filename ) ) {
308
+ $data = file_get_contents( $filename );
309
+ $config = @json_decode( $data, true );
310
+
311
+ if ( is_array( $config ) ) {
312
+ foreach ( $config as $key => $value )
313
+ $this->set( $key, $value );
314
+
315
+ return true;
316
+ }
317
+ }
318
+
319
+ return false;
320
+ }
321
+
322
+
323
+
324
+ public function get_md5() {
325
+ if ( is_null( $this->_md5 ) )
326
+ $this->_md5 = substr( md5( serialize( $this->_data ) ), 20 );
327
+ return $this->_md5;
328
+ }
329
+
330
+
331
+
332
+ /**
333
+ * Loads config.
334
+ * In a case it finds out config files are of older version - uses slower
335
+ * loader which takes all possible bloglevel-overloads into account
336
+ * correctly
337
+ */
338
+ public function load() {
339
+ $master_filename = Config::util_config_filename( 0, $this->_preview );
340
+ $data = Config::util_array_from_file( $master_filename );
341
+
342
+ // config file assumed is not up to date, use slow version
343
+ if ( !isset( $data['version'] ) || $data['version'] != W3TC_VERSION )
344
+ return $this->load_full();
345
+
346
+ if ( !$this->is_master() ) {
347
+ $child_filename = Config::util_config_filename( $this->_blog_id,
348
+ $this->_preview );
349
+ $child_data = Config::util_array_from_file( $child_filename );
350
+
351
+ if ( !is_null( $child_data ) ) {
352
+ if ( !isset( $data['version'] ) || $data['version'] != W3TC_VERSION )
353
+ return $this->load_full();
354
+
355
+ foreach ( $child_data as $key => $value )
356
+ $data[$key] = $value;
357
+ }
358
+ }
359
+
360
+ $this->_data = $data;
361
+ }
362
+
363
+
364
+
365
+ /**
366
+ * Slower version of loader, used when configs belong to older w3tc version
367
+ */
368
+ private function load_full() {
369
+ $c = new ConfigCompiler( $this->_blog_id, $this->_preview );
370
+ $c->load();
371
+ $this->_data = $c->get_data();
372
+ }
373
+ }
ConfigCompiler.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class ConfigCompiler {
5
+ private $_blog_id;
6
+ private $_preview;
7
+
8
+ private $_data;
9
+ private $_keys;
10
+
11
+
12
+
13
+ /**
14
+ * Returns true is key is not modifiable anymore
15
+ * Placed here to limit class loading
16
+ *
17
+ * @return bool
18
+ */
19
+ static public function child_key_sealed( $key, $master_data, $child_data ) {
20
+ if ( isset( $master_data['common.force_master'] ) &&
21
+ (boolean)$master_data['common.force_master'] )
22
+ return true;
23
+
24
+ // affects rules which are global, not possible to overload
25
+ if ( $key == 'pgcache.engine' &&
26
+ ( $master_data['pgcache.engine'] == 'file_generic' ) )
27
+ return true;
28
+ if ( in_array( $key, array(
29
+ 'minify.rewrite',
30
+ 'browsercache.rewrite',
31
+ 'version' ) ) )
32
+ return true;
33
+
34
+ include W3TC_DIR . '/ConfigKeys.php';
35
+
36
+ // key which marks overloads is always editable
37
+ $overloads_postfix = '.configuration_overloaded';
38
+
39
+ // extension settings sealing
40
+ // e.g. array('newrelic' , '...') is controlled
41
+ // by 'newrelic.configuration_overloaded']
42
+ $block_key = ( is_array( $key ) ? $key[0] : $key );
43
+
44
+ if ( isset( $child_data[$block_key] ) &&
45
+ isset( $child_data[$block_key . $overloads_postfix] ) &&
46
+ (boolean)$child_data[$block_key . $overloads_postfix] )
47
+ return false;
48
+
49
+ if ( !is_array( $key ) ) {
50
+ if ( substr( $key, strlen( $key ) - strlen( $overloads_postfix ),
51
+ strlen( $overloads_postfix ) ) == $overloads_postfix )
52
+ return false;
53
+
54
+ // default sealing
55
+ foreach ( $overloading_keys_scope as $i ) {
56
+ $overloading_key = $i['key'];
57
+
58
+ // check if this key is allowed by overloading-mark key
59
+ if ( substr( $key, 0, strlen( $i['prefix'] ) ) == $i['prefix'] ) {
60
+ if ( !isset( $child_data[$overloading_key] ) )
61
+ return true;
62
+ if ( (boolean)$child_data[$overloading_key] )
63
+ return false;
64
+ }
65
+ }
66
+ }
67
+
68
+ return true;
69
+ }
70
+
71
+
72
+
73
+ /**
74
+ * Reads config from file and returns it's content as array (or null)
75
+ * Stored in this class to limit class loading
76
+ */
77
+ static private function util_array_from_file_legacy( $filename ) {
78
+ if ( file_exists( $filename ) && is_readable( $filename ) ) {
79
+ // including file directly instead of read+eval causes constant
80
+ // problems with APC, ZendCache, and WSOD in a case of
81
+ // broken config file
82
+ $content = @file_get_contents( $filename );
83
+ $config = @eval( substr( $content, 5 ) );
84
+
85
+ if ( is_array( $config ) )
86
+ return $config;
87
+ }
88
+
89
+ return null;
90
+ }
91
+
92
+
93
+
94
+ public function __construct( $blog_id, $preview ) {
95
+ $this->_blog_id = $blog_id;
96
+ $this->_preview = $preview;
97
+
98
+ include W3TC_DIR . '/ConfigKeys.php';
99
+ $this->_keys = $keys;
100
+
101
+ // move _date to initial state
102
+ foreach ( $this->_keys as $key => $value )
103
+ $this->_data[$key] = $value['default'];
104
+
105
+ $this->_data['version'] = W3TC_VERSION;
106
+ }
107
+
108
+
109
+
110
+ public function load() {
111
+ // apply data from master config
112
+ $master_filename = Config::util_config_filename( 0, $this->_preview );
113
+ $data = Config::util_array_from_file( $master_filename );
114
+ if ( is_null( $data ) && $this->_preview ) {
115
+ // try to read production data when preview not available
116
+ $master_filename = Config::util_config_filename( 0, false );
117
+ $data = Config::util_array_from_file( $master_filename );
118
+ }
119
+
120
+ // try to get legacy data
121
+ if ( is_null( $data ) ) {
122
+ $master_filename = Config::util_config_filename_legacy( 0,
123
+ $this->_preview );
124
+ $data = self::util_array_from_file_legacy( $master_filename );
125
+ }
126
+
127
+ if ( is_array( $data ) ) {
128
+ $data = $this->upgrade( $data );
129
+ foreach ( $data as $key => $value )
130
+ $this->_data[$key] = $value;
131
+ }
132
+
133
+ if ( $this->is_master() )
134
+ return;
135
+
136
+
137
+ // apply child config
138
+ $child_filename = Config::util_config_filename( $this->_blog_id,
139
+ $this->_preview );
140
+ $data = Config::util_array_from_file( $child_filename );
141
+ if ( is_null( $data ) && $this->_preview ) {
142
+ // try to read production data when preview not available
143
+ $child_filename = Config::util_config_filename( $this->_blog_id,
144
+ false );
145
+ $data = Config::util_array_from_file( $child_filename );
146
+ }
147
+
148
+ // try to get legacy data
149
+ if ( is_null( $data ) ) {
150
+ $child_filename = Config::util_config_filename_legacy(
151
+ $this->_blog_id, $this->_preview );
152
+ $data = self::util_array_from_file_legacy( $child_filename );
153
+ }
154
+
155
+ if ( is_array( $data ) ) {
156
+ $data = $this->upgrade( $data );
157
+ foreach ( $data as $key => $value ) {
158
+ if ( !ConfigCompiler::child_key_sealed( $key, $this->_data, $data ) )
159
+ $this->_data[$key] = $value;
160
+ }
161
+ }
162
+ }
163
+
164
+
165
+
166
+ public function apply_data( $data ) {
167
+ foreach ( $data as $key => $value )
168
+ $this->_data[$key] = $value;
169
+ }
170
+
171
+
172
+
173
+ public function get_data() {
174
+ return $this->_data;
175
+ }
176
+
177
+
178
+
179
+ public function save() {
180
+ $data = array(
181
+ 'version' => $this->_data['version']
182
+ );
183
+
184
+ if ( $this->is_master() ) {
185
+ foreach ( $this->_data as $key => $value )
186
+ $data[$key] = $this->_data[$key];
187
+ } else {
188
+ // write only overwrited keys
189
+ $master = new ConfigCompiler( 0, $this->_preview );
190
+ $master->load();
191
+
192
+ foreach ( $this->_data as $key => $value ) {
193
+ if ( !ConfigCompiler::child_key_sealed( $key, $master->_data,
194
+ $this->_data ) )
195
+ $data[$key] = $this->_data[$key];
196
+ }
197
+ }
198
+
199
+ $filename = Config::util_config_filename( $this->_blog_id,
200
+ $this->_preview );
201
+ if ( defined( 'JSON_PRETTY_PRINT' ) )
202
+ $config = json_encode( $data, JSON_PRETTY_PRINT );
203
+ else // for older php versions
204
+ $config = json_encode( $data );
205
+
206
+ Util_File::file_put_contents_atomic( $filename, $config );
207
+ }
208
+
209
+
210
+ /**
211
+ * Returns true if we edit master config
212
+ *
213
+ * @return boolean
214
+ */
215
+ private function is_master() {
216
+ return $this->_blog_id <= 0;
217
+ }
218
+
219
+
220
+
221
+ /**
222
+ * Apply new default values when version changes
223
+ */
224
+ private function upgrade( $file_data ) {
225
+ if ( !isset( $file_data['version'] ) )
226
+ $file_data['version'] = '0.0.0';
227
+
228
+ if ( !function_exists( 'bb2_start' ) ) {
229
+ $file_data['pgcache.bad_behavior_path'] = '';
230
+ } else {
231
+ if ( file_exists( WP_PLUGIN_DIR . '/bad-behavior/bad-behavior-generic.php' ) ) {
232
+ $bb_file = WP_PLUGIN_DIR . '/bad-behavior/bad-behavior-generic.php';
233
+ } elseif ( file_exists( WP_PLUGIN_DIR . '/Bad-Behavior/bad-behavior-generic.php' ) ) {
234
+ $bb_file = WP_PLUGIN_DIR . '/Bad-Behavior/bad-behavior-generic.php';
235
+ } else {
236
+ $bb_file = false;
237
+ }
238
+
239
+ if ( $bb_file ) {
240
+ $file_data['pgcache.bad_behavior_path'] = $bb_file;
241
+ }
242
+ }
243
+
244
+ //
245
+ // changes in 0.9.5
246
+ //
247
+ if ( !isset( $file_data['extensions.active_frontend'] ) ||
248
+ !is_array( $file_data['extensions.active_frontend'] ) )
249
+ $file_data['extensions.active_frontend'] = array();
250
+
251
+ if ( version_compare( $file_data['version'], '0.9.5', '<' ) ) {
252
+ // dont show minify tips if already enabled
253
+ if ( isset( $file_data['minify.enabled'] ) &&
254
+ $file_data['minify.enabled'] == 'true' &&
255
+ function_exists( 'get_option' ) ) {
256
+ $cs = Dispatcher::config_state();
257
+ $cs->set( 'minify.hide_minify_help', true );
258
+ $cs->save();
259
+ }
260
+ $file_data['pgcache.mirrors.enabled'] = true;
261
+
262
+ // map regions in rackspace
263
+ if ( isset( $file_data['cdn.rscf.location'] ) ) {
264
+ if ( $file_data['cdn.rscf.location'] == 'uk' )
265
+ $file_data['cdn.rscf.location'] = 'LON';
266
+ if ( $file_data['cdn.rscf.location'] == 'us' )
267
+ $file_data['cdn.rscf.location'] = 'ORD';
268
+ }
269
+
270
+ // change filenames
271
+ $active = array();
272
+
273
+ if ( isset( $file_data['extensions.active'] ) &&
274
+ is_array( $file_data['extensions.active'] ) ) {
275
+ if ( isset( $file_data['extensions.active']['cloudflare'] ) )
276
+ $active['cloudflare'] = 'w3-total-cache/Extension_CloudFlare_Plugin.php';
277
+ if ( isset( $file_data['extensions.active']['feedburner'] ) )
278
+ $active['feedburner'] = 'w3-total-cache/Extension_FeedBurner_Plugin.php';
279
+ if ( isset( $file_data['extensions.active']['genesis.theme'] ) )
280
+ $active['genesis.theme'] = 'w3-total-cache/Extension_Genesis_Plugin.php';
281
+ if ( isset( $file_data['extensions.active']['wordpress-seo'] ) )
282
+ $active['wordpress-seo'] = 'w3-total-cache/Extension_WordPressSeo_Plugin.php';
283
+ }
284
+ $file_data['extensions.active'] = $active;
285
+
286
+ $active_frontend = array();
287
+ foreach ( $active as $key => $value )
288
+ $active_frontend[$key] = '*';
289
+
290
+ $file_data['extensions.active_frontend'] = $active_frontend;
291
+
292
+ // keep those active by default
293
+ $file_data['extensions.active']['newrelic'] =
294
+ 'w3-total-cache/Extension_NewRelic_Plugin.php';
295
+ $file_data['extensions.active']['fragmentcache'] =
296
+ 'w3-total-cache/Extension_FragmentCache_Plugin.php';
297
+
298
+ }
299
+
300
+ // newrelic settings - migrate to extension
301
+ if ( isset( $file_data['newrelic.enabled'] ) &&
302
+ $file_data['newrelic.enabled'] ) {
303
+ // make new relic extension enabled
304
+ if ( !isset( $file_data['extensions.active_frontend']['newrelic'] ) )
305
+ $file_data['extensions.active_frontend']['newrelic'] ='*';
306
+ }
307
+
308
+ if ( !isset( $file_data['newrelic'] ) ||
309
+ !is_array( $file_data['newrelic'] ) )
310
+ $file_data['newrelic'] = array(
311
+ 'monitoring_type' => 'apm'
312
+ );
313
+
314
+ $this->_set_if_exists( $file_data, 'newrelic.api_key',
315
+ 'newrelic', 'api_key' );
316
+ $this->_set_if_exists( $file_data, 'newrelic.appname',
317
+ 'newrelic', 'apm.application_name' );
318
+ $this->_set_if_exists( $file_data, 'newrelic.accept.logged_roles',
319
+ 'newrelic', 'accept.logged_roles' );
320
+ $this->_set_if_exists( $file_data, 'newrelic.accept.roles',
321
+ 'newrelic', 'accept.roles' );
322
+ $this->_set_if_exists( $file_data, 'newrelic.use_php_function',
323
+ 'newrelic', 'use_php_function' );
324
+ $this->_set_if_exists( $file_data, 'newrelic.cache_time',
325
+ 'newrelic', 'cache_time' );
326
+ $this->_set_if_exists( $file_data, 'newrelic.enable_xmit',
327
+ 'newrelic', 'enable_xmit' );
328
+ $this->_set_if_exists( $file_data, 'newrelic.include_rum',
329
+ 'newrelic', 'include_rum' );
330
+
331
+ // extensions - kept in separate key now
332
+ $this->_set_if_exists_extension( $file_data, 'cloudflare' );
333
+ $this->_set_if_exists_extension( $file_data, 'genesis.theme' );
334
+ $this->_set_if_exists_extension( $file_data, 'feedburner' );
335
+
336
+ // fragmentcache to extension
337
+ if ( isset( $file_data['fragmentcache.enabled'] ) &&
338
+ $file_data['fragmentcache.enabled'] ) {
339
+ // make new relic extension enabled
340
+ if ( !isset( $file_data['extensions.active_frontend']['fragmentcache'] ) )
341
+ $file_data['extensions.active_frontend']['fragmentcache'] = '*';
342
+ }
343
+
344
+ $this->_set_if_exists( $file_data, 'fragmentcache.debug',
345
+ 'fragmentcache', 'debug' );
346
+ $this->_set_if_exists( $file_data, 'fragmentcache.engine',
347
+ 'fragmentcache', 'engine' );
348
+ $this->_set_if_exists( $file_data, 'fragmentcache.file.gc',
349
+ 'fragmentcache', 'file.gc' );
350
+ $this->_set_if_exists( $file_data, 'fragmentcache.file.locking',
351
+ 'fragmentcache', 'file.locking' );
352
+ $this->_set_if_exists( $file_data, 'fragmentcache.memcached.servers',
353
+ 'fragmentcache', 'memcached.servers' );
354
+ $this->_set_if_exists( $file_data, 'fragmentcache.memcached.persistent',
355
+ 'fragmentcache', 'memcached.persistent' );
356
+ $this->_set_if_exists( $file_data, 'fragmentcache.memcached.aws_autodiscovery',
357
+ 'fragmentcache', 'memcached.aws_autodiscovery' );
358
+ $this->_set_if_exists( $file_data, 'fragmentcache.memcached.username',
359
+ 'fragmentcache', 'memcached.username' );
360
+ $this->_set_if_exists( $file_data, 'fragmentcache.memcached.password',
361
+ 'fragmentcache', 'memcached.password' );
362
+ $this->_set_if_exists( $file_data, 'fragmentcache.redis.persistent',
363
+ 'fragmentcache', 'redis.persistent' );
364
+ $this->_set_if_exists( $file_data, 'fragmentcache.redis.servers',
365
+ 'fragmentcache', 'redis.servers' );
366
+ $this->_set_if_exists( $file_data, 'fragmentcache.redis.password',
367
+ 'fragmentcache', 'redis.password' );
368
+ $this->_set_if_exists( $file_data, 'fragmentcache.redis.dbid',
369
+ 'fragmentcache', 'redis.dbid' );
370
+ $this->_set_if_exists( $file_data, 'fragmentcache.lifetime',
371
+ 'fragmentcache', 'lifetime' );
372
+
373
+ $file_data['version'] = W3TC_VERSION;
374
+
375
+ return $file_data;
376
+ }
377
+
378
+
379
+
380
+ private function _set_if_exists_extension( &$a, $extension ) {
381
+ if ( isset( $a['extensions.settings'] ) &&
382
+ isset( $a['extensions.settings'][$extension] ) ) {
383
+ $a[$extension] = $a['extensions.settings'][$extension];
384
+ unset( $a['extensions.settings'][$extension] );
385
+ }
386
+ }
387
+
388
+
389
+
390
+ private function _set_if_exists( &$a, $old_key, $new_key0, $new_key1 ) {
391
+ if ( isset( $a[$old_key] ) ) {
392
+ $a[$new_key0][$new_key1] = $a[$old_key];
393
+ unset( $a[$old_key] );
394
+ }
395
+ }
396
+ }
ConfigKeys.php ADDED
@@ -0,0 +1,1917 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Descriptors of configuration keys
5
+ * for config
6
+ *
7
+ * Reminder: The maximum length of keys cannot exceed 64 chars. This is the limit for the name attribute in form fields.
8
+ */
9
+
10
+ $keys = array(
11
+ 'cluster.messagebus.debug' => array(
12
+ 'type' => 'boolean',
13
+ 'default' => false
14
+ ),
15
+ 'cluster.messagebus.enabled' => array(
16
+ 'type' => 'boolean',
17
+ 'default' => false
18
+ ),
19
+ 'cluster.messagebus.sns.region' => array(
20
+ 'type' => 'string',
21
+ 'default' => ''
22
+ ),
23
+ 'cluster.messagebus.sns.api_key' => array(
24
+ 'type' => 'string',
25
+ 'default' => ''
26
+ ),
27
+ 'cluster.messagebus.sns.api_secret' => array(
28
+ 'type' => 'string',
29
+ 'default' => ''
30
+ ),
31
+ 'cluster.messagebus.sns.topic_arn' => array(
32
+ 'type' => 'string',
33
+ 'default' => ''
34
+ ),
35
+
36
+ 'dbcache.configuration_overloaded' => array(
37
+ 'type' => 'boolean',
38
+ 'default' => false
39
+ ),
40
+ 'dbcache.debug' => array(
41
+ 'type' => 'boolean',
42
+ 'default' => false
43
+ ),
44
+ 'dbcache.enabled' => array(
45
+ 'type' => 'boolean',
46
+ 'default' => false
47
+ ),
48
+ 'dbcache.engine' => array(
49
+ 'type' => 'string',
50
+ 'default' => 'file'
51
+ ),
52
+ 'dbcache.file.gc' => array(
53
+ 'type' => 'integer',
54
+ 'default' => 3600
55
+ ),
56
+ 'dbcache.file.locking' => array(
57
+ 'type' => 'boolean',
58
+ 'default' => false
59
+ ),
60
+ 'dbcache.lifetime' => array(
61
+ 'type' => 'integer',
62
+ 'default' => 180
63
+ ),
64
+ 'dbcache.memcached.persistent' => array(
65
+ 'type' => 'boolean',
66
+ 'default' => true
67
+ ),
68
+ 'dbcache.memcached.aws_autodiscovery' => array(
69
+ 'type' => 'boolean',
70
+ 'default' => false
71
+ ),
72
+ 'dbcache.memcached.servers' => array(
73
+ 'type' => 'array',
74
+ 'default' => array(
75
+ '127.0.0.1:11211'
76
+ )
77
+ ),
78
+ 'dbcache.memcached.username' => array(
79
+ 'type' => 'string',
80
+ 'default' => ''
81
+ ),
82
+ 'dbcache.memcached.password' => array(
83
+ 'type' => 'string',
84
+ 'default' => ''
85
+ ),
86
+ 'dbcache.redis.persistent' => array(
87
+ 'type' => 'boolean',
88
+ 'default' => true
89
+ ),
90
+ 'dbcache.redis.servers' => array(
91
+ 'type' => 'array',
92
+ 'default' => array(
93
+ '127.0.0.1:6379'
94
+ )
95
+ ),
96
+ 'dbcache.redis.password' => array(
97
+ 'type' => 'string',
98
+ 'default' => ''
99
+ ),
100
+ 'dbcache.redis.dbid' => array(
101
+ 'type' => 'integer',
102
+ 'default' => 0
103
+ ),
104
+ 'dbcache.reject.constants' => array(
105
+ 'type' => 'array',
106
+ 'default' => array(
107
+ 'APP_REQUEST',
108
+ 'DOING_CRON',
109
+ 'DONOTCACHEDB',
110
+ 'SHORTINIT', // WPMU and WP 3.0 short init
111
+ 'XMLRPC_REQUEST'
112
+ )
113
+ ),
114
+ 'dbcache.reject.cookie' => array(
115
+ 'type' => 'array',
116
+ 'default' => array()
117
+ ),
118
+ 'dbcache.reject.logged' => array(
119
+ 'type' => 'boolean',
120
+ 'default' => true
121
+ ),
122
+ 'dbcache.reject.sql' => array(
123
+ 'type' => 'array',
124
+ 'default' => array(
125
+ 'gdsr_',
126
+ 'wp_rg_',
127
+ '_wp_session_'
128
+ )
129
+ ),
130
+ 'dbcache.reject.uri' => array(
131
+ 'type' => 'array',
132
+ 'default' => array()
133
+ ),
134
+ 'dbcache.reject.words' => array(
135
+ 'type' => 'array',
136
+ 'default' => array(
137
+ '^\s*insert\b',
138
+ '^\s*delete\b',
139
+ '^\s*update\b',
140
+ '^\s*replace\b',
141
+ '^\s*create\b',
142
+ '^\s*alter\b',
143
+ '^\s*show\b',
144
+ '^\s*set\b',
145
+ '\bautoload\s+=\s+\'yes\'',
146
+ '\bsql_calc_found_rows\b',
147
+ '\bfound_rows\(\)'
148
+ )
149
+ ),
150
+
151
+ 'objectcache.configuration_overloaded' => array(
152
+ 'type' => 'boolean',
153
+ 'default' => false
154
+ ),
155
+ 'objectcache.enabled' => array(
156
+ 'type' => 'boolean',
157
+ 'default' => false
158
+ ),
159
+ 'objectcache.debug' => array(
160
+ 'type' => 'boolean',
161
+ 'default' => false
162
+ ),
163
+ 'objectcache.engine' => array(
164
+ 'type' => 'string',
165
+ 'default' => 'file'
166
+ ),
167
+ 'objectcache.file.gc' => array(
168
+ 'type' => 'integer',
169
+ 'default' => 3600
170
+ ),
171
+ 'objectcache.file.locking' => array(
172
+ 'type' => 'boolean',
173
+ 'default' => false
174
+ ),
175
+ 'objectcache.memcached.servers' => array(
176
+ 'type' => 'array',
177
+ 'default' => array(
178
+ '127.0.0.1:11211'
179
+ )
180
+ ),
181
+ 'objectcache.memcached.persistent' => array(
182
+ 'type' => 'boolean',
183
+ 'default' => true
184
+ ),
185
+ 'objectcache.memcached.aws_autodiscovery' => array(
186
+ 'type' => 'boolean',
187
+ 'default' => false
188
+ ),
189
+ 'objectcache.memcached.username' => array(
190
+ 'type' => 'string',
191
+ 'default' => ''
192
+ ),
193
+ 'objectcache.memcached.password' => array(
194
+ 'type' => 'string',
195
+ 'default' => ''
196
+ ),
197
+ 'objectcache.redis.persistent' => array(
198
+ 'type' => 'boolean',
199
+ 'default' => true
200
+ ),
201
+ 'objectcache.redis.servers' => array(
202
+ 'type' => 'array',
203
+ 'default' => array(
204
+ '127.0.0.1:6379'
205
+ )
206
+ ),
207
+ 'objectcache.redis.password' => array(
208
+ 'type' => 'string',
209
+ 'default' => ''
210
+ ),
211
+ 'objectcache.redis.dbid' => array(
212
+ 'type' => 'integer',
213
+ 'default' => 0
214
+ ),
215
+ 'objectcache.groups.global' => array(
216
+ 'type' => 'array',
217
+ 'default' => array(
218
+ 'users',
219
+ 'userlogins',
220
+ 'usermeta',
221
+ 'user_meta',
222
+ 'site-transient',
223
+ 'site-options',
224
+ 'site-lookup',
225
+ 'blog-lookup',
226
+ 'blog-details',
227
+ 'rss',
228
+ 'global-posts'
229
+ )
230
+ ),
231
+ 'objectcache.groups.nonpersistent' => array(
232
+ 'type' => 'array',
233
+ 'default' => array(
234
+ 'comment',
235
+ 'counts',
236
+ 'plugins'
237
+ )
238
+ ),
239
+ 'objectcache.lifetime' => array(
240
+ 'type' => 'integer',
241
+ 'default' => 180
242
+ ),
243
+ 'objectcache.purge.all' => array(
244
+ 'type' => 'boolean',
245
+ 'default' => false
246
+ ),
247
+
248
+ 'pgcache.configuration_overloaded' => array(
249
+ 'type' => 'boolean',
250
+ 'default' => false
251
+ ),
252
+ 'pgcache.enabled' => array(
253
+ 'type' => 'boolean',
254
+ 'default' => false
255
+ ),
256
+ 'pgcache.comment_cookie_ttl' => array(
257
+ 'type' => 'integer',
258
+ 'default' => 1800
259
+ ),
260
+ 'pgcache.debug' => array(
261
+ 'type' => 'boolean',
262
+ 'default' => false
263
+ ),
264
+ 'pgcache.engine' => array(
265
+ 'type' => 'string',
266
+ 'default' => 'file_generic'
267
+ ),
268
+ 'pgcache.file.gc' => array(
269
+ 'type' => 'integer',
270
+ 'default' => 3600
271
+ ),
272
+ 'pgcache.file.nfs' => array(
273
+ 'type' => 'boolean',
274
+ 'default' => false
275
+ ),
276
+ 'pgcache.file.locking' => array(
277
+ 'type' => 'boolean',
278
+ 'default' => false
279
+ ),
280
+ 'pgcache.lifetime' => array(
281
+ 'type' => 'integer',
282
+ 'default' => 3600
283
+ ),
284
+ 'pgcache.memcached.servers' => array(
285
+ 'type' => 'array',
286
+ 'default' => array(
287
+ '127.0.0.1:11211'
288
+ )
289
+ ),
290
+ 'pgcache.memcached.persistent' => array(
291
+ 'type' => 'boolean',
292
+ 'default' => true
293
+ ),
294
+ 'pgcache.memcached.aws_autodiscovery' => array(
295
+ 'type' => 'boolean',
296
+ 'default' => false
297
+ ),
298
+ 'pgcache.memcached.username' => array(
299
+ 'type' => 'string',
300
+ 'default' => ''
301
+ ),
302
+ 'pgcache.memcached.password' => array(
303
+ 'type' => 'string',
304
+ 'default' => ''
305
+ ),
306
+ 'pgcache.redis.persistent' => array(
307
+ 'type' => 'boolean',
308
+ 'default' => true
309
+ ),
310
+ 'pgcache.redis.servers' => array(
311
+ 'type' => 'array',
312
+ 'default' => array(
313
+ '127.0.0.1:6379'
314
+ )
315
+ ),
316
+ 'pgcache.redis.password' => array(
317
+ 'type' => 'string',
318
+ 'default' => ''
319
+ ),
320
+ 'pgcache.redis.dbid' => array(
321
+ 'type' => 'integer',
322
+ 'default' => 0
323
+ ),
324
+ 'pgcache.cache.query' => array(
325
+ 'type' => 'boolean',
326
+ 'default' => true
327
+ ),
328
+ 'pgcache.cache.home' => array(
329
+ 'type' => 'boolean',
330
+ 'default' => true
331
+ ),
332
+ 'pgcache.cache.feed' => array(
333
+ 'type' => 'boolean',
334
+ 'default' => false
335
+ ),
336
+ 'pgcache.cache.nginx_handle_xml' => array(
337
+ 'type' => 'boolean',
338
+ 'default' => false
339
+ ),
340
+ 'pgcache.cache.ssl' => array(
341
+ 'type' => 'boolean',
342
+ 'default' => false
343
+ ),
344
+ 'pgcache.cache.404' => array(
345
+ 'type' => 'boolean',
346
+ 'default' => false
347
+ ),
348
+ 'pgcache.cache.headers' => array(
349
+ 'type' => 'array',
350
+ 'default' => array(
351
+ 'Last-Modified',
352
+ 'Content-Type',
353
+ 'X-Pingback',
354
+ 'P3P'
355
+ )
356
+ ),
357
+ 'pgcache.compatibility' => array(
358
+ 'type' => 'boolean',
359
+ 'default' => false
360
+ ),
361
+ 'pgcache.remove_charset' => array(
362
+ 'type' => 'boolean',
363
+ 'default' => false
364
+ ),
365
+ 'pgcache.accept.uri' => array(
366
+ 'type' => 'array',
367
+ 'default' => array(
368
+ 'sitemap(_index)?\.xml(\.gz)?',
369
+ '([a-z0-9_\-]+)?sitemap\.xsl',
370
+ '[a-z0-9_\-]+-sitemap([0-9]+)?\.xml(\.gz)?'
371
+ )
372
+ ),
373
+ 'pgcache.accept.files' => array(
374
+ 'type' => 'array',
375
+ 'default' => array(
376
+ 'wp-comments-popup.php',
377
+ 'wp-links-opml.php',
378
+ 'wp-locations.php'
379
+ )
380
+ ),
381
+ 'pgcache.accept.qs' => array(
382
+ 'type' => 'array',
383
+ 'default' => array()
384
+ ),
385
+ 'pgcache.late_init' => array(
386
+ 'type' => 'boolean',
387
+ 'default' => false
388
+ ),
389
+ 'pgcache.late_caching' => array(
390
+ 'type' => 'boolean',
391
+ 'default' => false
392
+ ),
393
+ 'pgcache.mirrors.enabled' => array(
394
+ 'type' => 'boolean',
395
+ 'default' => false
396
+ ),
397
+ 'pgcache.mirrors.home_urls' => array(
398
+ 'type' => 'array',
399
+ 'default' => array()
400
+ ),
401
+ 'pgcache.reject.front_page' => array(
402
+ 'type' => 'boolean',
403
+ 'default' => false
404
+ ),
405
+ 'pgcache.reject.logged' => array(
406
+ 'type' => 'boolean',
407
+ 'default' => true
408
+ ),
409
+ 'pgcache.reject.logged_roles' => array(
410
+ 'type' => 'boolean',
411
+ 'default' => false
412
+ ),
413
+ 'pgcache.reject.roles' => array(
414
+ 'type' => 'array',
415
+ 'default' => array()
416
+ ),
417
+ 'pgcache.reject.uri' => array(
418
+ 'type' => 'array',
419
+ 'default' => array(
420
+ 'wp-.*\.php',
421
+ 'index\.php'
422
+ )
423
+ ),
424
+ 'pgcache.reject.ua' => array(
425
+ 'type' => 'array',
426
+ 'default' => array()
427
+ ),
428
+ 'pgcache.reject.cookie' => array(
429
+ 'type' => 'array',
430
+ 'default' => array( 'wptouch_switch_toggle' )
431
+ ),
432
+ 'pgcache.reject.request_head' => array(
433
+ 'type' => 'boolean',
434
+ 'default' => false
435
+ ),
436
+ 'pgcache.purge.front_page' => array(
437
+ 'type' => 'boolean',
438
+ 'default' => false
439
+ ),
440
+ 'pgcache.purge.home' => array(
441
+ 'type' => 'boolean',
442
+ 'default' => true
443
+ ),
444
+ 'pgcache.purge.post' => array(
445
+ 'type' => 'boolean',
446
+ 'default' => true
447
+ ),
448
+ 'pgcache.purge.comments' => array(
449
+ 'type' => 'boolean',
450
+ 'default' => false
451
+ ),
452
+ 'pgcache.purge.author' => array(
453
+ 'type' => 'boolean',
454
+ 'default' => false
455
+ ),
456
+ 'pgcache.purge.terms' => array(
457
+ 'type' => 'boolean',
458
+ 'default' => false
459
+ ),
460
+ 'pgcache.purge.archive.daily' => array(
461
+ 'type' => 'boolean',
462
+ 'default' => false
463
+ ),
464
+ 'pgcache.purge.archive.monthly' => array(
465
+ 'type' => 'boolean',
466
+ 'default' => false
467
+ ),
468
+ 'pgcache.purge.archive.yearly' => array(
469
+ 'type' => 'boolean',
470
+ 'default' => false
471
+ ),
472
+ 'pgcache.purge.feed.blog' => array(
473
+ 'type' => 'boolean',
474
+ 'default' => true
475
+ ),
476
+ 'pgcache.purge.feed.comments' => array(
477
+ 'type' => 'boolean',
478
+ 'default' => false
479
+ ),
480
+ 'pgcache.purge.feed.author' => array(
481
+ 'type' => 'boolean',
482
+ 'default' => false
483
+ ),
484
+ 'pgcache.purge.feed.terms' => array(
485
+ 'type' => 'boolean',
486
+ 'default' => false
487
+ ),
488
+ 'pgcache.purge.feed.types' => array(
489
+ 'type' => 'array',
490
+ 'default' => array(
491
+ 'rss2'
492
+ )
493
+ ),
494
+ 'pgcache.purge.postpages_limit' => array(
495
+ 'type' => 'integer',
496
+ 'default' => 10
497
+ ),
498
+ 'pgcache.purge.pages' => array(
499
+ 'type' => 'array',
500
+ 'default' => array()
501
+ ),
502
+ 'pgcache.purge.sitemap_regex' => array(
503
+ 'type' => 'string',
504
+ 'default' => '([a-z0-9_\-]*?)sitemap([a-z0-9_\-]*)?\.xml'
505
+ ),
506
+ 'pgcache.prime.enabled' => array(
507
+ 'type' => 'boolean',
508
+ 'default' => false
509
+ ),
510
+ 'pgcache.prime.interval' => array(
511
+ 'type' => 'integer',
512
+ 'default' => 900
513
+ ),
514
+ 'pgcache.prime.limit' => array(
515
+ 'type' => 'integer',
516
+ 'default' => 10
517
+ ),
518
+ 'pgcache.prime.sitemap' => array(
519
+ 'type' => 'string',
520
+ 'default' => ''
521
+ ),
522
+ 'pgcache.prime.post.enabled' => array(
523
+ 'type' => 'boolean',
524
+ 'default' => false
525
+ ),
526
+
527
+ 'stats.enabled' => array(
528
+ 'type' => 'boolean',
529
+ 'default' => false
530
+ ),
531
+
532
+ 'minify.configuration_overloaded' => array(
533
+ 'type' => 'boolean',
534
+ 'default' => false
535
+ ),
536
+ 'minify.enabled' => array(
537
+ 'type' => 'boolean',
538
+ 'default' => false
539
+ ),
540
+ 'minify.auto' => array(
541
+ 'type' => 'boolean',
542
+ 'default' => true
543
+ ),
544
+ 'minify.debug' => array(
545
+ 'type' => 'boolean',
546
+ 'default' => false
547
+ ),
548
+ 'minify.engine' => array(
549
+ 'type' => 'string',
550
+ 'default' => 'file'
551
+ ),
552
+ 'minify.error.notification' => array(
553
+ 'type' => 'string',
554
+ 'default' => ''
555
+ ),
556
+ 'minify.file.gc' => array(
557
+ 'type' => 'integer',
558
+ 'default' => 86400
559
+ ),
560
+ 'minify.file.nfs' => array(
561
+ 'type' => 'boolean',
562
+ 'default' => false
563
+ ),
564
+ 'minify.file.locking' => array(
565
+ 'type' => 'boolean',
566
+ 'default' => false
567
+ ),
568
+ 'minify.memcached.servers' => array(
569
+ 'type' => 'array',
570
+ 'default' => array(
571
+ '127.0.0.1:11211'
572
+ )
573
+ ),
574
+ 'minify.memcached.persistent' => array(
575
+ 'type' => 'boolean',
576
+ 'default' => true
577
+ ),
578
+ 'minify.memcached.aws_autodiscovery' => array(
579
+ 'type' => 'boolean',
580
+ 'default' => false
581
+ ),
582
+ 'minify.memcached.username' => array(
583
+ 'type' => 'string',
584
+ 'default' => ''
585
+ ),
586
+ 'minify.memcached.password' => array(
587
+ 'type' => 'string',
588
+ 'default' => ''
589
+ ),
590
+ 'minify.redis.persistent' => array(
591
+ 'type' => 'boolean',
592
+ 'default' => true
593
+ ),
594
+ 'minify.redis.servers' => array(
595
+ 'type' => 'array',
596
+ 'default' => array(
597
+ '127.0.0.1:6379'
598
+ )
599
+ ),
600
+ 'minify.redis.password' => array(
601
+ 'type' => 'string',
602
+ 'default' => ''
603
+ ),
604
+ 'minify.redis.dbid' => array(
605
+ 'type' => 'integer',
606
+ 'default' => 0
607
+ ),
608
+ 'minify.rewrite' => array(
609
+ 'type' => 'boolean',
610
+ 'default' => true
611
+ ),
612
+ 'minify.options' => array(
613
+ 'type' => 'array',
614
+ 'default' => array()
615
+ ),
616
+ 'minify.symlinks' => array(
617
+ 'type' => 'array',
618
+ 'default' => array()
619
+ ),
620
+ 'minify.lifetime' => array(
621
+ 'type' => 'integer',
622
+ 'default' => 86400
623
+ ),
624
+ 'minify.upload' => array(
625
+ 'type' => 'boolean',
626
+ 'default' => true
627
+ ),
628
+ 'minify.html.enable' => array(
629
+ 'type' => 'boolean',
630
+ 'default' => false
631
+ ),
632
+ 'minify.html.engine' => array(
633
+ 'type' => 'string',
634
+ 'default' => 'html'
635
+ ),
636
+ 'minify.html.reject.feed' => array(
637
+ 'type' => 'boolean',
638
+ 'default' => false
639
+ ),
640
+ 'minify.html.inline.css' => array(
641
+ 'type' => 'boolean',
642
+ 'default' => false
643
+ ),
644
+ 'minify.html.inline.js' => array(
645
+ 'type' => 'boolean',
646
+ 'default' => false
647
+ ),
648
+ 'minify.html.strip.crlf' => array(
649
+ 'type' => 'boolean',
650
+ 'default' => false
651
+ ),
652
+ 'minify.html.comments.ignore' => array(
653
+ 'type' => 'array',
654
+ 'default' => array(
655
+ 'google_ad_',
656
+ 'RSPEAK_'
657
+ )
658
+ ),
659
+ 'minify.css.enable' => array(
660
+ 'type' => 'boolean',
661
+ 'default' => true
662
+ ),
663
+ 'minify.css.engine' => array(
664
+ 'type' => 'string',
665
+ 'default' => 'css'
666
+ ),
667
+ 'minify.css.combine' => array(
668
+ 'type' => 'boolean',
669
+ 'default' => false
670
+ ),
671
+ 'minify.css.strip.comments' => array(
672
+ 'type' => 'boolean',
673
+ 'default' => false
674
+ ),
675
+ 'minify.css.strip.crlf' => array(
676
+ 'type' => 'boolean',
677
+ 'default' => false
678
+ ),
679
+ 'minify.css.embed' => array(
680
+ 'type' => 'boolean',
681
+ 'default' => false
682
+ ),
683
+ 'minify.css.imports' => array(
684
+ 'type' => 'string',
685
+ 'default' => ''
686
+ ),
687
+ 'minify.css.groups' => array(
688
+ 'type' => 'array',
689
+ 'default' => array()
690
+ ),
691
+ 'minify.js.enable' => array(
692
+ 'type' => 'boolean',
693
+ 'default' => true
694
+ ),
695
+ 'minify.js.engine' => array(
696
+ 'type' => 'string',
697
+ 'default' => 'js'
698
+ ),
699
+ 'minify.js.combine.header' => array(
700
+ 'type' => 'boolean',
701
+ 'default' => false
702
+ ),
703
+ 'minify.js.header.embed_type' => array(
704
+ 'type' => 'string',
705
+ 'default' => 'blocking'
706
+ ),
707
+ 'minify.js.combine.body' => array(
708
+ 'type' => 'boolean',
709
+ 'default' => false
710
+ ),
711
+ 'minify.js.body.embed_type' => array(
712
+ 'type' => 'string',
713
+ 'default' => 'blocking'
714
+ ),
715
+ 'minify.js.combine.footer' => array(
716
+ 'type' => 'boolean',
717
+ 'default' => false
718
+ ),
719
+ 'minify.js.footer.embed_type' => array(
720
+ 'type' => 'string',
721
+ 'default' => 'blocking'
722
+ ),
723
+ 'minify.js.strip.comments' => array(
724
+ 'type' => 'boolean',
725
+ 'default' => false
726
+ ),
727
+ 'minify.js.strip.crlf' => array(
728
+ 'type' => 'boolean',
729
+ 'default' => false
730
+ ),
731
+ 'minify.js.groups' => array(
732
+ 'type' => 'array',
733
+ 'default' => array()
734
+ ),
735
+ 'minify.yuijs.path.java' => array(
736
+ 'type' => 'string',
737
+ 'default' => 'java'
738
+ ),
739
+ 'minify.yuijs.path.jar' => array(
740
+ 'type' => 'string',
741
+ 'default' => 'yuicompressor.jar'
742
+ ),
743
+ 'minify.yuijs.options.line-break' => array(
744
+ 'type' => 'integer',
745
+ 'default' => 5000
746
+ ),
747
+ 'minify.yuijs.options.nomunge' => array(
748
+ 'type' => 'boolean',
749
+ 'default' => false
750
+ ),
751
+ 'minify.yuijs.options.preserve-semi' => array(
752
+ 'type' => 'boolean',
753
+ 'default' => false
754
+ ),
755
+ 'minify.yuijs.options.disable-optimizations' => array(
756
+ 'type' => 'boolean',
757
+ 'default' => false
758
+ ),
759
+ 'minify.yuicss.path.java' => array(
760
+ 'type' => 'string',
761
+ 'default' => 'java'
762
+ ),
763
+ 'minify.yuicss.path.jar' => array(
764
+ 'type' => 'string',
765
+ 'default' => 'yuicompressor.jar'
766
+ ),
767
+ 'minify.yuicss.options.line-break' => array(
768
+ 'type' => 'integer',
769
+ 'default' => 5000
770
+ ),
771
+ 'minify.ccjs.path.java' => array(
772
+ 'type' => 'string',
773
+ 'default' => 'java'
774
+ ),
775
+ 'minify.ccjs.path.jar' => array(
776
+ 'type' => 'string',
777
+ 'default' => 'compiler.jar'
778
+ ),
779
+ 'minify.ccjs.options.compilation_level' => array(
780
+ 'type' => 'string',
781
+ 'default' => 'SIMPLE_OPTIMIZATIONS'
782
+ ),
783
+ 'minify.ccjs.options.formatting' => array(
784
+ 'type' => 'string',
785
+ 'default' => ''
786
+ ),
787
+ 'minify.csstidy.options.remove_bslash' => array(
788
+ 'type' => 'boolean',
789
+ 'default' => true
790
+ ),
791
+ 'minify.csstidy.options.compress_colors' => array(
792
+ 'type' => 'boolean',
793
+ 'default' => true
794
+ ),
795
+ 'minify.csstidy.options.compress_font-weight' => array(
796
+ 'type' => 'boolean',
797
+ 'default' => true
798
+ ),
799
+ 'minify.csstidy.options.lowercase_s' => array(
800
+ 'type' => 'boolean',
801
+ 'default' => false
802
+ ),
803
+ 'minify.csstidy.options.optimise_shorthands' => array(
804
+ 'type' => 'integer',
805
+ 'default' => 1
806
+ ),
807
+ 'minify.csstidy.options.remove_last_;' => array(
808
+ 'type' => 'boolean',
809
+ 'default' => false
810
+ ),
811
+ 'minify.csstidy.options.case_properties' => array(
812
+ 'type' => 'integer',
813
+ 'default' => 1
814
+ ),
815
+ 'minify.csstidy.options.sort_properties' => array(
816
+ 'type' => 'boolean',
817
+ 'default' => false
818
+ ),
819
+ 'minify.csstidy.options.sort_selectors' => array(
820
+ 'type' => 'boolean',
821
+ 'default' => false
822
+ ),
823
+ 'minify.csstidy.options.merge_selectors' => array(
824
+ 'type' => 'integer',
825
+ 'default' => 2
826
+ ),
827
+ 'minify.csstidy.options.discard_invalid_properties' => array(
828
+ 'type' => 'boolean',
829
+ 'default' => false
830
+ ),
831
+ 'minify.csstidy.options.css_level' => array(
832
+ 'type' => 'string',
833
+ 'default' => 'CSS2.1'
834
+ ),
835
+ 'minify.csstidy.options.preserve_css' => array(
836
+ 'type' => 'boolean',
837
+ 'default' => false
838
+ ),
839
+ 'minify.csstidy.options.timestamp' => array(
840
+ 'type' => 'boolean',
841
+ 'default' => false
842
+ ),
843
+ 'minify.csstidy.options.template' => array(
844
+ 'type' => 'string',
845
+ 'default' => 'default'
846
+ ),
847
+ 'minify.htmltidy.options.clean' => array(
848
+ 'type' => 'boolean',
849
+ 'default' => false
850
+ ),
851
+ 'minify.htmltidy.options.hide-comments' => array(
852
+ 'type' => 'boolean',
853
+ 'default' => true
854
+ ),
855
+ 'minify.htmltidy.options.wrap' => array(
856
+ 'type' => 'integer',
857
+ 'default' => 0
858
+ ),
859
+ 'minify.reject.logged' => array(
860
+ 'type' => 'boolean',
861
+ 'default' => false
862
+ ),
863
+ 'minify.reject.ua' => array(
864
+ 'type' => 'array',
865
+ 'default' => array()
866
+ ),
867
+ 'minify.reject.uri' => array(
868
+ 'type' => 'array',
869
+ 'default' => array()
870
+ ),
871
+ 'minify.reject.files.js' => array(
872
+ 'type' => 'array',
873
+ 'default' => array()
874
+ ),
875
+ 'minify.reject.files.css' => array(
876
+ 'type' => 'array',
877
+ 'default' => array()
878
+ ),
879
+ 'minify.cache.files' => array(
880
+ 'type' => 'array',
881
+ 'default' => array( 'https://ajax.googleapis.com' )
882
+ ),
883
+
884
+ 'cdn.configuration_overloaded' => array(
885
+ 'type' => 'boolean',
886
+ 'default' => false
887
+ ),
888
+ 'cdn.enabled' => array(
889
+ 'type' => 'boolean',
890
+ 'default' => false
891
+ ),
892
+ 'cdn.debug' => array(
893
+ 'type' => 'boolean',
894
+ 'default' => false
895
+ ),
896
+ 'cdn.engine' => array(
897
+ 'type' => 'string',
898
+ 'default' => 'maxcdn'
899
+ ),
900
+ 'cdn.uploads.enable' => array(
901
+ 'type' => 'boolean',
902
+ 'default' => true
903
+ ),
904
+ 'cdn.includes.enable' => array(
905
+ 'type' => 'boolean',
906
+ 'default' => true
907
+ ),
908
+ 'cdn.includes.files' => array(
909
+ 'type' => 'string',
910
+ 'default' => '*.css;*.js;*.gif;*.png;*.jpg;*.xml'
911
+ ),
912
+ 'cdn.theme.enable' => array(
913
+ 'type' => 'boolean',
914
+ 'default' => true
915
+ ),
916
+ 'cdn.theme.files' => array(
917
+ 'type' => 'string',
918
+ 'default' => '*.css;*.js;*.gif;*.png;*.jpg;*.ico;*.ttf;*.otf,*.woff,*.less'
919
+ ),
920
+ 'cdn.minify.enable' => array(
921
+ 'type' => 'boolean',
922
+ 'default' => true
923
+ ),
924
+ 'cdn.custom.enable' => array(
925
+ 'type' => 'boolean',
926
+ 'default' => true
927
+ ),
928
+ 'cdn.custom.files' => array(
929
+ 'type' => 'array',
930
+ 'default' => array(
931
+ 'favicon.ico',
932
+ '{wp_content_dir}/gallery/*',
933
+ '{wp_content_dir}/uploads/avatars/*',
934
+ '{plugins_dir}/wordpress-seo/css/xml-sitemap.xsl',
935
+ '{plugins_dir}/wp-minify/min*',
936
+ '{plugins_dir}/*.js',
937
+ '{plugins_dir}/*.css',
938
+ '{plugins_dir}/*.gif',
939
+ '{plugins_dir}/*.jpg',
940
+ '{plugins_dir}/*.png',
941
+ )
942
+ ),
943
+ 'cdn.import.files' => array(
944
+ 'type' => 'string',
945
+ 'default' => false
946
+ ),
947
+ 'cdn.queue.interval' => array(
948
+ 'type' => 'integer',
949
+ 'default' => 900
950
+ ),
951
+ 'cdn.queue.limit' => array(
952
+ 'type' => 'integer',
953
+ 'default' => 25
954
+ ),
955
+ 'cdn.force.rewrite' => array(
956
+ 'type' => 'boolean',
957
+ 'default' => false
958
+ ),
959
+ 'cdn.autoupload.enabled' => array(
960
+ 'type' => 'boolean',
961
+ 'default' => false
962
+ ),
963
+ 'cdn.autoupload.interval' => array(
964
+ 'type' => 'integer',
965
+ 'default' => 3600
966
+ ),
967
+ 'cdn.canonical_header' => array(
968
+ 'type' => 'boolean',
969
+ 'default' => false
970
+ ),
971
+
972
+ 'cdn.ftp.host' => array(
973
+ 'type' => 'string',
974
+ 'default' => ''
975
+ ),
976
+ 'cdn.ftp.type' => array(
977
+ 'type' => 'string',
978
+ 'default' => ''
979
+ ),
980
+ 'cdn.ftp.user' => array(
981
+ 'type' => 'string',
982
+ 'default' => ''
983
+ ),
984
+ 'cdn.ftp.pass' => array(
985
+ 'type' => 'string',
986
+ 'default' => ''
987
+ ),
988
+ 'cdn.ftp.path' => array(
989
+ 'type' => 'string',
990
+ 'default' => ''
991
+ ),
992
+ 'cdn.ftp.pasv' => array(
993
+ 'type' => 'boolean',
994
+ 'default' => false
995
+ ),
996
+ 'cdn.ftp.domain' => array(
997
+ 'type' => 'array',
998
+ 'default' => array()
999
+ ),
1000
+ 'cdn.ftp.ssl' => array(
1001
+ 'type' => 'string',
1002
+ 'default' => 'auto'
1003
+ ),
1004
+
1005
+ 'cdn.google_drive.client_id' => array(
1006
+ 'type' => 'string',
1007
+ 'default' => ''
1008
+ ),
1009
+ 'cdn.google_drive.refresh_token' => array(
1010
+ 'type' => 'string',
1011
+ 'default' => ''
1012
+ ),
1013
+ 'cdn.google_drive.folder.id' => array(
1014
+ 'type' => 'string',
1015
+ 'default' => ''
1016
+ ),
1017
+ 'cdn.google_drive.folder.title' => array(
1018
+ 'type' => 'string',
1019
+ 'default' => ''
1020
+ ),
1021
+ 'cdn.google_drive.folder.url' => array(
1022
+ 'type' => 'string',
1023
+ 'default' => ''
1024
+ ),
1025
+
1026
+ 'cdn.highwinds.account_hash' => array(
1027
+ 'type' => 'string',
1028
+ 'default' => ''
1029
+ ),
1030
+ 'cdn.highwinds.api_token' => array(
1031
+ 'type' => 'string',
1032
+ 'default' => ''
1033
+ ),
1034
+ 'cdn.highwinds.host.hash_code' => array(
1035
+ 'type' => 'string',
1036
+ 'default' => ''
1037
+ ),
1038
+ 'cdn.highwinds.host.domains' => array(
1039
+ 'type' => 'array',
1040
+ 'default' => array()
1041
+ ),
1042
+ 'cdn.highwinds.ssl' => array(
1043
+ 'type' => 'string',
1044
+ 'default' => 'auto'
1045
+ ),
1046
+
1047
+ 'cdn.s3.key' => array(
1048
+ 'type' => 'string',
1049
+ 'default' => ''
1050
+ ),
1051
+ 'cdn.s3.secret' => array(
1052
+ 'type' => 'string',
1053
+ 'default' => ''
1054
+ ),
1055
+ 'cdn.s3.bucket' => array(
1056
+ 'type' => 'string',
1057
+ 'default' => ''
1058
+ ),
1059
+ 'cdn.s3.cname' => array(
1060
+ 'type' => 'array',
1061
+ 'default' => array()
1062
+ ),
1063
+ 'cdn.s3.ssl' => array(
1064
+ 'type' => 'string',
1065
+ 'default' => 'auto'
1066
+ ),
1067
+
1068
+ 'cdn.s3_compatible.api_host' => array(
1069
+ 'type' => 'string',
1070
+ 'default' => 'auto'
1071
+ ),
1072
+
1073
+ 'cdn.cf.key' => array(
1074
+ 'type' => 'string',
1075
+ 'default' => ''
1076
+ ),
1077
+ 'cdn.cf.secret' => array(
1078
+ 'type' => 'string',
1079
+ 'default' => ''
1080
+ ),
1081
+ 'cdn.cf.bucket' => array(
1082
+ 'type' => 'string',
1083
+ 'default' => ''
1084
+ ),
1085
+ 'cdn.cf.id' => array(
1086
+ 'type' => 'string',
1087
+ 'default' => ''
1088
+ ),
1089
+ 'cdn.cf.cname' => array(
1090
+ 'type' => 'array',
1091
+ 'default' => array()
1092
+ ),
1093
+ 'cdn.cf.ssl' => array(
1094
+ 'type' => 'string',
1095
+ 'default' => 'auto'
1096
+ ),
1097
+ 'cdn.cf2.key' => array(
1098
+ 'type' => 'string',
1099
+ 'default' => ''
1100
+ ),
1101
+ 'cdn.cf2.secret' => array(
1102
+ 'type' => 'string',
1103
+ 'default' => ''
1104
+ ),
1105
+ 'cdn.cf2.id' => array(
1106
+ 'type' => 'string',
1107
+ 'default' => ''
1108
+ ),
1109
+ 'cdn.cf2.cname' => array(
1110
+ 'type' => 'array',
1111
+ 'default' => array()
1112
+ ),
1113
+ 'cdn.cf2.ssl' => array(
1114
+ 'type' => 'string',
1115
+ 'default' => ''
1116
+ ),
1117
+ 'cdn.rscf.user' => array(
1118
+ 'type' => 'string',
1119
+ 'default' => ''
1120
+ ),
1121
+ 'cdn.rscf.key' => array(
1122
+ 'type' => 'string',
1123
+ 'default' => ''
1124
+ ),
1125
+ 'cdn.rscf.location' => array(
1126
+ 'type' => 'string',
1127
+ 'default' => 'us'
1128
+ ),
1129
+ 'cdn.rscf.container' => array(
1130
+ 'type' => 'string',
1131
+ 'default' => ''
1132
+ ),
1133
+ 'cdn.rscf.cname' => array(
1134
+ 'type' => 'array',
1135
+ 'default' => array()
1136
+ ),
1137
+ 'cdn.rscf.ssl' => array(
1138
+ 'type' => 'string',
1139
+ 'default' => 'auto'
1140
+ ),
1141
+ 'cdn.rackspace_cdn.user_name' => array(
1142
+ 'type' => 'string',
1143
+ 'default' => ''
1144
+ ),
1145
+ 'cdn.rackspace_cdn.api_key' => array(
1146
+ 'type' => 'string',
1147
+ 'default' => ''
1148
+ ),
1149
+ 'cdn.rackspace_cdn.region' => array(
1150
+ 'type' => 'string',
1151
+ 'default' => ''
1152
+ ),
1153
+ 'cdn.rackspace_cdn.service.access_url' => array(
1154
+ 'type' => 'string',
1155
+ 'default' => ''
1156
+ ),
1157
+ 'cdn.rackspace_cdn.service.id' => array(
1158
+ 'type' => 'string',
1159
+ 'default' => ''
1160
+ ),
1161
+ 'cdn.rackspace_cdn.service.name' => array(
1162
+ 'type' => 'string',
1163
+ 'default' => ''
1164
+ ),
1165
+ 'cdn.rackspace_cdn.service.protocol' => array(
1166
+ 'type' => 'string',
1167
+ 'default' => 'http'
1168
+ ),
1169
+ 'cdn.rackspace_cdn.domains' => array(
1170
+ 'type' => 'array',
1171
+ 'default' => array()
1172
+ ),
1173
+
1174
+ 'cdn.azure.user' => array(
1175
+ 'type' => 'string',
1176
+ 'default' => ''
1177
+ ),
1178
+ 'cdn.azure.key' => array(
1179
+ 'type' => 'string',
1180
+ 'default' => ''
1181
+ ),
1182
+ 'cdn.azure.container' => array(
1183
+ 'type' => 'string',
1184
+ 'default' => ''
1185
+ ),
1186
+ 'cdn.azure.cname' => array(
1187
+ 'type' => 'array',
1188
+ 'default' => array()
1189
+ ),
1190
+ 'cdn.azure.ssl' => array(
1191
+ 'type' => 'string',
1192
+ 'default' => 'auto'
1193
+ ),
1194
+ 'cdn.mirror.domain' => array(
1195
+ 'type' => 'array',
1196
+ 'default' => array()
1197
+ ),
1198
+ 'cdn.mirror.ssl' => array(
1199
+ 'type' => 'string',
1200
+ 'default' => 'auto'
1201
+ ),
1202
+ 'cdn.netdna.alias' => array(
1203
+ 'type' => 'string',
1204
+ 'default' => ''
1205
+ ),
1206
+ 'cdn.netdna.consumerkey' => array(
1207
+ 'type' => 'string',
1208
+ 'default' => ''
1209
+ ),
1210
+ 'cdn.netdna.consumersecret' => array(
1211
+ 'type' => 'string',
1212
+ 'default' => ''
1213
+ ),
1214
+ 'cdn.netdna.authorization_key' => array(
1215
+ 'type' => 'string',
1216
+ 'default' => ''
1217
+ ),
1218
+ 'cdn.netdna.domain' => array(
1219
+ 'type' => 'array',
1220
+ 'default' => array()
1221
+ ),
1222
+ 'cdn.netdna.ssl' => array(
1223
+ 'type' => 'string',
1224
+ 'default' => 'auto'
1225
+ ),
1226
+ 'cdn.netdna.zone_id' => array(
1227
+ 'type' => 'integer',
1228
+ 'default' => 0
1229
+ ),
1230
+ 'cdn.maxcdn.authorization_key' => array(
1231
+ 'type' => 'string',
1232
+ 'default' => ''
1233
+ ),
1234
+ 'cdn.maxcdn.domain' => array(
1235
+ 'type' => 'array',
1236
+ 'default' => array()
1237
+ ),
1238
+ 'cdn.maxcdn.ssl' => array(
1239
+ 'type' => 'string',
1240
+ 'default' => 'auto'
1241
+ ),
1242
+ 'cdn.maxcdn.zone_id' => array(
1243
+ 'type' => 'integer',
1244
+ 'default' => 0
1245
+ ),
1246
+ 'cdn.cotendo.username' => array(
1247
+ 'type' => 'string',
1248
+ 'default' => ''
1249
+ ),
1250
+ 'cdn.cotendo.password' => array(
1251
+ 'type' => 'string',
1252
+ 'default' => ''
1253
+ ),
1254
+ 'cdn.cotendo.zones' => array(
1255
+ 'type' => 'array',
1256
+ 'default' => array()
1257
+ ),
1258
+ 'cdn.cotendo.domain' => array(
1259
+ 'type' => 'array',
1260
+ 'default' => array()
1261
+ ),
1262
+ 'cdn.cotendo.ssl' => array(
1263
+ 'type' => 'string',
1264
+ 'default' => 'auto'
1265
+ ),
1266
+ 'cdn.akamai.username' => array(
1267
+ 'type' => 'string',
1268
+ 'default' => ''
1269
+ ),
1270
+ 'cdn.akamai.password' => array(
1271
+ 'type' => 'string',
1272
+ 'default' => ''
1273
+ ),
1274
+ 'cdn.akamai.email_notification' => array(
1275
+ 'type' => 'array',
1276
+ 'default' => array()
1277
+ ),
1278
+ 'cdn.akamai.action' => array(
1279
+ 'type' => 'string',
1280
+ 'default' => 'invalidate'
1281
+ ),
1282
+ 'cdn.akamai.zone' => array(
1283
+ 'type' => 'string',
1284
+ 'default' => 'production'
1285
+ ),
1286
+ 'cdn.akamai.domain' => array(
1287
+ 'type' => 'array',
1288
+ 'default' => array()
1289
+ ),
1290
+ 'cdn.akamai.ssl' => array(
1291
+ 'type' => 'string',
1292
+ 'default' => 'auto'
1293
+ ),
1294
+ 'cdn.edgecast.account' => array(
1295
+ 'type' => 'string',
1296
+ 'default' => ''
1297
+ ),
1298
+ 'cdn.edgecast.token' => array(
1299
+ 'type' => 'string',
1300
+ 'default' => ''
1301
+ ),
1302
+ 'cdn.edgecast.domain' => array(
1303
+ 'type' => 'array',
1304
+ 'default' => array()
1305
+ ),
1306
+ 'cdn.edgecast.ssl' => array(
1307
+ 'type' => 'string',
1308
+ 'default' => 'auto'
1309
+ ),
1310
+ 'cdn.att.account' => array(
1311
+ 'type' => 'string',
1312
+ 'default' => ''
1313
+ ),
1314
+ 'cdn.att.token' => array(
1315
+ 'type' => 'string',
1316
+ 'default' => ''
1317
+ ),
1318
+ 'cdn.att.domain' => array(
1319
+ 'type' => 'array',
1320
+ 'default' => array()
1321
+ ),
1322
+ 'cdn.att.ssl' => array(
1323
+ 'type' => 'string',
1324
+ 'default' => 'auto'
1325
+ ),
1326
+ 'cdn.reject.admins' => array(
1327
+ 'type' => 'boolean',
1328
+ 'default' => false
1329
+ ),
1330
+ 'cdn.reject.logged_roles' => array(
1331
+ 'type' => 'boolean',
1332
+ 'default' => false
1333
+ ),
1334
+ 'cdn.reject.roles' => array(
1335
+ 'type' => 'array',
1336
+ 'default' => array()
1337
+ ),
1338
+ 'cdn.reject.ua' => array(
1339
+ 'type' => 'array',
1340
+ 'default' => array()
1341
+ ),
1342
+ 'cdn.reject.uri' => array(
1343
+ 'type' => 'array',
1344
+ 'default' => array()
1345
+ ),
1346
+ 'cdn.reject.files' => array(
1347
+ 'type' => 'array',
1348
+ 'default' => array(
1349
+ '{uploads_dir}/wpcf7_captcha/*',
1350
+ '{uploads_dir}/imagerotator.swf',
1351
+ '{plugins_dir}/wp-fb-autoconnect/facebook-platform/channel.html'
1352
+ )
1353
+ ),
1354
+ 'cdn.reject.ssl' => array(
1355
+ 'type' => 'boolean',
1356
+ 'default' => false
1357
+ ),
1358
+ 'varnish.configuration_overloaded' => array(
1359
+ 'type' => 'boolean',
1360
+ 'default' => false
1361
+ ),
1362
+ 'varnish.enabled' => array(
1363
+ 'type' => 'boolean',
1364
+ 'default' => false
1365
+ ),
1366
+ 'varnish.debug' => array(
1367
+ 'type' => 'boolean',
1368
+ 'default' => false
1369
+ ),
1370
+ 'varnish.servers' => array(
1371
+ 'type' => 'array',
1372
+ 'default' => array()
1373
+ ),
1374
+
1375
+ 'browsercache.configuration_overloaded' => array(
1376
+ 'type' => 'boolean',
1377
+ 'default' => false
1378
+ ),
1379
+ 'browsercache.enabled' => array(
1380
+ 'type' => 'boolean',
1381
+ 'default' => true
1382
+ ),
1383
+ 'browsercache.rewrite' => array(
1384
+ 'type' => 'boolean',
1385
+ 'default' => false
1386
+ ),
1387
+ 'browsercache.hsts' => array(
1388
+ 'type' => 'boolean',
1389
+ 'default' => false
1390
+ ),
1391
+ 'browsercache.no404wp' => array(
1392
+ 'type' => 'boolean',
1393
+ 'default' => false
1394
+ ),
1395
+ 'browsercache.no404wp.exceptions' => array(
1396
+ 'type' => 'array',
1397
+ 'default' => array(
1398
+ 'robots\.txt',
1399
+ '[a-z0-9_\-]*sitemap[a-z0-9_\-]*\.(xml|xsl|html)(\.gz)?'
1400
+ )
1401
+ ),
1402
+ 'browsercache.cssjs.last_modified' => array(
1403
+ 'type' => 'boolean',
1404
+ 'default' => true
1405
+ ),
1406
+ 'browsercache.cssjs.compression' => array(
1407
+ 'type' => 'boolean',
1408
+ 'default' => true
1409
+ ),
1410
+ 'browsercache.cssjs.expires' => array(
1411
+ 'type' => 'boolean',
1412
+ 'default' => false
1413
+ ),
1414
+ 'browsercache.cssjs.lifetime' => array(
1415
+ 'type' => 'integer',
1416
+ 'default' => 31536000
1417
+ ),
1418
+ 'browsercache.cssjs.nocookies' => array(
1419
+ 'type' => 'boolean',
1420
+ 'default' => false
1421
+ ),
1422
+ 'browsercache.cssjs.cache.control' => array(
1423
+ 'type' => 'boolean',
1424
+ 'default' => false
1425
+ ),
1426
+ 'browsercache.cssjs.cache.policy' => array(
1427
+ 'type' => 'string',
1428
+ 'default' => 'cache_public_maxage'
1429
+ ),
1430
+ 'browsercache.cssjs.etag' => array(
1431
+ 'type' => 'boolean',
1432
+ 'default' => false
1433
+ ),
1434
+ 'browsercache.cssjs.w3tc' => array(
1435
+ 'type' => 'boolean',
1436
+ 'default' => false
1437
+ ),
1438
+ 'browsercache.cssjs.replace' => array(
1439
+ 'type' => 'boolean',
1440
+ 'default' => false
1441
+ ),
1442
+ 'browsercache.html.compression' => array(
1443
+ 'type' => 'boolean',
1444
+ 'default' => true
1445
+ ),
1446
+ 'browsercache.html.last_modified' => array(
1447
+ 'type' => 'boolean',
1448
+ 'default' => true
1449
+ ),
1450
+ 'browsercache.html.expires' => array(
1451
+ 'type' => 'boolean',
1452
+ 'default' => false
1453
+ ),
1454
+ 'browsercache.html.lifetime' => array(
1455
+ 'type' => 'integer',
1456
+ 'default' => 3600
1457
+ ),
1458
+ 'browsercache.html.cache.control' => array(
1459
+ 'type' => 'boolean',
1460
+ 'default' => false
1461
+ ),
1462
+ 'browsercache.html.cache.policy' => array(
1463
+ 'type' => 'string',
1464
+ 'default' => 'cache_public_maxage'
1465
+ ),
1466
+ 'browsercache.html.etag' => array(
1467
+ 'type' => 'boolean',
1468
+ 'default' => false
1469
+ ),
1470
+ 'browsercache.html.w3tc' => array(
1471
+ 'type' => 'boolean',
1472
+ 'default' => false
1473
+ ),
1474
+ 'browsercache.html.replace' => array(
1475
+ 'type' => 'boolean',
1476
+ 'default' => false
1477
+ ),
1478
+ 'browsercache.other.last_modified' => array(
1479
+ 'type' => 'boolean',
1480
+ 'default' => true
1481
+ ),
1482
+ 'browsercache.other.compression' => array(
1483
+ 'type' => 'boolean',
1484
+ 'default' => true
1485
+ ),
1486
+ 'browsercache.other.expires' => array(
1487
+ 'type' => 'boolean',
1488
+ 'default' => false
1489
+ ),
1490
+ 'browsercache.other.lifetime' => array(
1491
+ 'type' => 'integer',
1492
+ 'default' => 31536000
1493
+ ),
1494
+ 'browsercache.other.nocookies' => array(
1495
+ 'type' => 'boolean',
1496
+ 'default' => false
1497
+ ),
1498
+ 'browsercache.other.cache.control' => array(
1499
+ 'type' => 'boolean',
1500
+ 'default' => false
1501
+ ),
1502
+ 'browsercache.other.cache.policy' => array(
1503
+ 'type' => 'string',
1504
+ 'default' => 'cache_public_maxage'
1505
+ ),
1506
+ 'browsercache.other.etag' => array(
1507
+ 'type' => 'boolean',
1508
+ 'default' => false
1509
+ ),
1510
+ 'browsercache.other.w3tc' => array(
1511
+ 'type' => 'boolean',
1512
+ 'default' => false
1513
+ ),
1514
+ 'browsercache.other.replace' => array(
1515
+ 'type' => 'boolean',
1516
+ 'default' => false
1517
+ ),
1518
+ 'browsercache.replace.exceptions' => array (
1519
+ 'type' => 'array',
1520
+ 'default' => array()
1521
+ ),
1522
+
1523
+ 'mobile.configuration_overloaded' => array(
1524
+ 'type' => 'boolean',
1525
+ 'default' => false
1526
+ ),
1527
+ 'mobile.enabled' => array(
1528
+ 'type' => 'boolean',
1529
+ 'default' => false
1530
+ ),
1531
+ 'mobile.rgroups' => array(
1532
+ 'type' => 'array',
1533
+ 'default' => array(
1534
+ 'high' => array(
1535
+ 'theme' => '',
1536
+ 'enabled' => false,
1537
+ 'redirect' => '',
1538
+ 'agents' => array(
1539
+ 'android',
1540
+ 'mobi',
1541
+ 'bada',
1542
+ 'incognito',
1543
+ 'kindle',
1544
+ 'maemo',
1545
+ 'opera\ mini',
1546
+ 's8000',
1547
+ 'series60',
1548
+ 'ucbrowser',
1549
+ 'ucweb',
1550
+ 'webmate',
1551
+ 'webos'
1552
+ )
1553
+ ),
1554
+ 'low' => array(
1555
+ 'theme' => '',
1556
+ 'enabled' => false,
1557
+ 'redirect' => '',
1558
+ 'agents' => array(
1559
+ '2\.0\ mmp',
1560
+ '240x320',
1561
+ 'alcatel',
1562
+ 'amoi',
1563
+ 'asus',
1564
+ 'au\-mic',
1565
+ 'audiovox',
1566
+ 'avantgo',
1567
+ 'benq',
1568
+ 'bird',
1569
+ 'blackberry',
1570
+ 'blazer',
1571
+ 'cdm',
1572
+ 'cellphone',
1573
+ 'danger',
1574
+ 'ddipocket',
1575
+ 'docomo',
1576
+ 'dopod',
1577
+ 'elaine/3\.0',
1578
+ 'ericsson',
1579
+ 'eudoraweb',
1580
+ 'fly',
1581
+ 'haier',
1582
+ 'hiptop',
1583
+ 'hp\.ipaq',
1584
+ 'htc',
1585
+ 'huawei',
1586
+ 'i\-mobile',
1587
+ 'iemobile',
1588
+ 'iemobile/7',
1589
+ 'iemobile/9',
1590
+ 'j\-phone',
1591
+ 'kddi',
1592
+ 'konka',
1593
+ 'kwc',
1594
+ 'kyocera/wx310k',
1595
+ 'lenovo',
1596
+ 'lg',
1597
+ 'lg/u990',
1598
+ 'lge\ vx',
1599
+ 'midp',
1600
+ 'midp\-2\.0',
1601
+ 'mmef20',
1602
+ 'mmp',
1603
+ 'mobilephone',
1604
+ 'mot\-v',
1605
+ 'motorola',
1606
+ 'msie\ 10\.0',
1607
+ 'netfront',
1608
+ 'newgen',
1609
+ 'newt',
1610
+ 'nintendo\ ds',
1611
+ 'nintendo\ wii',
1612
+ 'nitro',
1613
+ 'nokia',
1614
+ 'novarra',
1615
+ 'o2',
1616
+ 'openweb',
1617
+ 'opera\ mobi',
1618
+ 'opera\.mobi',
1619
+ 'p160u',
1620
+ 'palm',
1621
+ 'panasonic',
1622
+ 'pantech',
1623
+ 'pdxgw',
1624
+ 'pg',
1625
+ 'philips',
1626
+ 'phone',
1627
+ 'playbook',
1628
+ 'playstation\ portable',
1629
+ 'portalmmm',
1630
+ '\bppc\b',
1631
+ 'proxinet',
1632
+ 'psp',
1633
+ 'qtek',
1634
+ 'sagem',
1635
+ 'samsung',
1636
+ 'sanyo',
1637
+ 'sch',
1638
+ 'sch\-i800',
1639
+ 'sec',
1640
+ 'sendo',
1641
+ 'sgh',
1642
+ 'sharp',
1643
+ 'sharp\-tq\-gx10',
1644
+ 'small',
1645
+ 'smartphone',
1646
+ 'softbank',
1647
+ 'sonyericsson',
1648
+ 'sph',
1649
+ 'symbian',
1650
+ 'symbian\ os',
1651
+ 'symbianos',
1652
+ 'toshiba',
1653
+ 'treo',
1654
+ 'ts21i\-10',
1655
+ 'up\.browser',
1656
+ 'up\.link',
1657
+ 'uts',
1658
+ 'vertu',
1659
+ 'vodafone',
1660
+ 'wap',
1661
+ 'willcome',
1662
+ 'windows\ ce',
1663
+ 'windows\.ce',
1664
+ 'winwap',
1665
+ 'xda',
1666
+ 'xoom',
1667
+ 'zte'
1668
+ )
1669
+ )
1670
+ )
1671
+ ),
1672
+
1673
+
1674
+ 'referrer.configuration_overloaded' => array(
1675
+ 'type' => 'boolean',
1676
+ 'default' => false
1677
+ ),
1678
+ 'referrer.enabled' => array(
1679
+ 'type' => 'boolean',
1680
+ 'default' => false
1681
+ ),
1682
+ 'referrer.rgroups' => array(
1683
+ 'type' => 'array',
1684
+ 'default' => array(
1685
+ 'search_engines' => array(
1686
+ 'theme' => '',
1687
+ 'enabled' => false,
1688
+ 'redirect' => '',
1689
+ 'referrers' => array(
1690
+ 'google\.com',
1691
+ 'yahoo\.com',
1692
+ 'bing\.com',
1693
+ 'ask\.com',
1694
+ 'msn\.com'
1695
+ )
1696
+ )
1697
+ )
1698
+ ),
1699
+
1700
+
1701
+ 'common.edge' => array(
1702
+ 'type' => 'boolean',
1703
+ 'default' => false
1704
+ ),
1705
+ 'common.support' => array(
1706
+ 'type' => 'string',
1707
+ 'default' => ''
1708
+ ),
1709
+ 'common.track_usage' => array(
1710
+ 'type' => 'boolean',
1711
+ 'default' => false
1712
+ ),
1713
+ 'common.tweeted' => array(
1714
+ 'type' => 'boolean',
1715
+ 'default' => false
1716
+ ),
1717
+ 'config.check' => array(
1718
+ 'type' => 'boolean',
1719
+ 'default' => true
1720
+ ),
1721
+ 'config.path' => array(
1722
+ 'type' => 'string',
1723
+ 'default' => ''
1724
+ ),
1725
+ 'widget.latest.items' => array(
1726
+ 'type' => 'integer',
1727
+ 'default' => 3
1728
+ ),
1729
+ 'widget.latest_news.items' => array(
1730
+ 'type' => 'integer',
1731
+ 'default' => 5
1732
+ ),
1733
+ 'widget.pagespeed.enabled' => array(
1734
+ 'type' => 'boolean',
1735
+ 'default' => true
1736
+ ),
1737
+ 'widget.pagespeed.key' => array(
1738
+ 'type' => 'string',
1739
+ 'default' => ''
1740
+ ),
1741
+ 'widget.pagespeed.show_in_admin_bar' => array(
1742
+ 'type' => 'boolean',
1743
+ 'default' => false
1744
+ ),
1745
+ 'timelimit.email_send' => array(
1746
+ 'type' => 'integer',
1747
+ 'default' => 180
1748
+ ),
1749
+ 'timelimit.varnish_purge' => array(
1750
+ 'type' => 'integer',
1751
+ 'default' => 300
1752
+ ),
1753
+ 'timelimit.cache_flush' => array(
1754
+ 'type' => 'integer',
1755
+ 'default' => 600
1756
+ ),
1757
+ 'timelimit.cache_gc' => array(
1758
+ 'type' => 'integer',
1759
+ 'default' => 600
1760
+ ),
1761
+ 'timelimit.cdn_upload' => array(
1762
+ 'type' => 'integer',
1763
+ 'default' => 600
1764
+ ),
1765
+ 'timelimit.cdn_delete' => array(
1766
+ 'type' => 'integer',
1767
+ 'default' => 300
1768
+ ),
1769
+ 'timelimit.cdn_purge' => array(
1770
+ 'type' => 'integer',
1771
+ 'default' => 300
1772
+ ),
1773
+ 'timelimit.cdn_import' => array(
1774
+ 'type' => 'integer',
1775
+ 'default' => 600
1776
+ ),
1777
+ 'timelimit.cdn_test' => array(
1778
+ 'type' => 'integer',
1779
+ 'default' => 300
1780
+ ),
1781
+ 'timelimit.cdn_container_create' => array(
1782
+ 'type' => 'integer',
1783
+ 'default' => 300
1784
+ ),
1785
+ 'timelimit.domain_rename' => array(
1786
+ 'type' => 'integer',
1787
+ 'default' => 120
1788
+ ),
1789
+ 'timelimit.minify_recommendations' => array(
1790
+ 'type' => 'integer',
1791
+ 'default' => 600
1792
+ ),
1793
+ 'common.instance_id' => array(
1794
+ 'type' => 'integer',
1795
+ 'default' => 0
1796
+ ),
1797
+ 'common.force_master' => array(
1798
+ 'type' => 'boolean',
1799
+ 'default' => true,
1800
+ 'master_only' => 'true'
1801
+ ),
1802
+
1803
+ 'extensions.active' => array(
1804
+ 'type' => 'array',
1805
+ 'default' => array(
1806
+ 'newrelic' => 'w3-total-cache/Extension_NewRelic_Plugin.php',
1807
+ 'fragmentcache' => 'w3-total-cache/Extension_FragmentCache_Plugin.php'
1808
+ )
1809
+ ),
1810
+ 'extensions.active_frontend' => array(
1811
+ 'type' => 'array',
1812
+ 'default' => array()
1813
+ ),
1814
+ 'plugin.license_key' => array(
1815
+ 'type' => 'string',
1816
+ 'default' => '',
1817
+ 'master_only' => true
1818
+ ),
1819
+ 'plugin.type' => array(
1820
+ 'type' => 'string',
1821
+ 'default' => '',
1822
+ 'master_only' => true
1823
+ ),
1824
+
1825
+
1826
+ // extensions keys:
1827
+ //
1828
+ // cloudflare =>
1829
+ // 'enabled'
1830
+ // 'email'
1831
+ // 'key'
1832
+ // 'zone'
1833
+ // 'widget_interval' => '30'
1834
+ // 'widget_cache_mins' => '5'
1835
+
1836
+ // genesis.theme =>
1837
+ // 'wp_head' => '0',
1838
+ // 'genesis_header' => '1',
1839
+ // 'genesis_do_nav' => '0',
1840
+ // 'genesis_do_subnav' => '0',
1841
+ // 'loop_front_page' => '1',
1842
+ // 'loop_terms' => '1',
1843
+ // 'flush_terms' => '1',
1844
+ // 'loop_single' => '1',
1845
+ // 'loop_single_excluded' => '',
1846
+ // 'loop_single_genesis_comments' => '0',
1847
+ // 'loop_single_genesis_pings' => '0',
1848
+ // 'sidebar' => '0',
1849
+ // 'sidebar_excluded' => '',
1850
+ // 'genesis_footer' => '1',
1851
+ // 'wp_footer' => '0',
1852
+ // 'reject_logged_roles' => '1',
1853
+ // 'reject_logged_roles_on_actions' => array(
1854
+ // 0 => 'genesis_loop',
1855
+ // 1 => 'wp_head',
1856
+ // 2 => 'wp_footer',
1857
+ // ),
1858
+ // 'reject_roles' => array(
1859
+ // 0 => 'administrator',
1860
+ // ),
1861
+ //
1862
+ // feedbuner =>
1863
+ // 'urls'
1864
+ //
1865
+ // newrelic.configuration_overloaded
1866
+ // newrelic => array
1867
+ // 'api_key' => '',
1868
+ // 'monitoring_type' => 'apm',
1869
+ // 'browser.application_id' => '',
1870
+ // 'apm.application_name' => '',
1871
+ // 'accept.logged_roles' => true,
1872
+ // 'accept.roles' => array('contributor'),
1873
+ // 'use_php_function' => true,
1874
+ // 'cache_time' => 5,
1875
+ // 'enable_xmit' => false,
1876
+ // 'include_rum' => true
1877
+ );
1878
+
1879
+
1880
+
1881
+ /*
1882
+ * Descriptors how sealed configuration keys affect overriding
1883
+ */
1884
+ $overloading_keys_scope = array(
1885
+ array(
1886
+ 'key' => 'browsercache.configuration_overloaded',
1887
+ 'prefix' => 'browsercache.'
1888
+ ),
1889
+ array(
1890
+ 'key' => 'cdn.configuration_overloaded',
1891
+ 'prefix' => 'cdn.'
1892
+ ),
1893
+ array(
1894
+ 'key' => 'dbcache.configuration_overloaded',
1895
+ 'prefix' => 'dbcache.'
1896
+ ),
1897
+ array(
1898
+ 'key' => 'minify.configuration_overloaded',
1899
+ 'prefix' => 'minify.'
1900
+ ),
1901
+ array(
1902
+ 'key' => 'objectcache.configuration_overloaded',
1903
+ 'prefix' => 'objectcache.'
1904
+ ),
1905
+ array(
1906
+ 'key' => 'fragmentcache.configuration_overloaded',
1907
+ 'prefix' => 'fragmentcache.'
1908
+ ),
1909
+ array(
1910
+ 'key' => 'pgcache.configuration_overloaded',
1911
+ 'prefix' => 'pgcache.'
1912
+ ),
1913
+ array(
1914
+ 'key' => 'varnish.configuration_overloaded',
1915
+ 'prefix' => 'varnish.'
1916
+ )
1917
+ );
ConfigState.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Provides state information - state can be changed by plugin during lifetime,
6
+ * while configuration is static
7
+ *
8
+ * master keys:
9
+ * common.install - time() of plugin installation
10
+ * common.support_us_invitations - number of invitations to support us shown
11
+ * common.next_support_us_invitation - time() of next support us invitation
12
+ * common.edge_invitations
13
+ * common.next_edge_invitation
14
+ * common.hide_note_wp_content_permissions
15
+ * common.hide_note_no_zlib
16
+ * common.hide_note_zlib_output_compression
17
+ * common.show_note.nginx_restart_required
18
+ * minify.error.file
19
+ * minify.error.last
20
+ * minify.error.notification.last
21
+ * minify.show_note_minify_error
22
+ * minify.hide_minify_help
23
+ * extension.cloudflare.next_ips_check
24
+ * extension.cloudflare.ips.ip4
25
+ * extension.cloudflare.ips.ip6
26
+ *
27
+ * blog-level keys:
28
+ * newrelic.hide_note_pageload_slow
29
+ * minify.show_note.need_flush
30
+ * minify.show_note.need_flush.timestamp - when the note was set
31
+ * cdn.hide_note_maxcdn_whitelist_ip
32
+ * cdn.hide_note_no_curl
33
+ * cdn.google_drive.access_token
34
+ * cdn.rackspace_cf.access_state
35
+ * cdn.rackspace_cdn.access_state
36
+ * cdn.show_note_theme_changed
37
+ * cdn.show_note_wp_upgraded
38
+ * cdn.show_note_cdn_upload
39
+ * cdn.show_note_cdn_reupload
40
+ * common.hide_note_no_permalink_rules
41
+ * common.show_note.plugins_updated
42
+ * common.show_note.plugins_updated.timestamp - when the note was set
43
+ * common.show_note.flush_statics_needed
44
+ * common.show_note.flush_statics_needed.timestamp
45
+ * common.show_note.flush_posts_needed
46
+ * common.show_note.flush_posts_needed.timestamp - when the note was set
47
+ * objectcache.show_note.flush_needed
48
+ * objectcache.show_note.flush_needed.timestamp - when the note was set
49
+ * extension.<extension_id>.hide_note_suggest_activation
50
+ * track.maxcdn_signup
51
+ * track.maxcdn_authorize
52
+ * track.maxcdn_validation
53
+ */
54
+ class ConfigState {
55
+ private $_data;
56
+ private $_is_master;
57
+
58
+
59
+
60
+ /**
61
+ * Constructor
62
+ */
63
+ public function __construct( $is_master ) {
64
+ $this->_is_master = $is_master;
65
+
66
+ if ( $is_master )
67
+ $data_raw = get_site_option( 'w3tc_state' );
68
+ else
69
+ $data_raw = get_option( 'w3tc_state' );
70
+
71
+ $this->_data = @json_decode( $data_raw, true );
72
+ if ( !is_array( $this->_data ) ) {
73
+ $this->_data = array();
74
+ $this->apply_defaults();
75
+ $this->save();
76
+ }
77
+ }
78
+
79
+
80
+
81
+ /**
82
+ * Returns value
83
+ *
84
+ * @param string $key
85
+ * @param string $default
86
+ * @return mixed
87
+ */
88
+ public function get( $key, $default ) {
89
+ if ( !isset( $this->_data[$key] ) )
90
+ return $default;
91
+
92
+ return $this->_data[$key];
93
+ }
94
+
95
+
96
+
97
+ /**
98
+ * Returns string value
99
+ *
100
+ * @param string $key
101
+ * @param string $default
102
+ * @param boolean $trim
103
+ * @return string
104
+ */
105
+ public function get_string( $key, $default = '', $trim = true ) {
106
+ $value = (string)$this->get( $key, $default );
107
+
108
+ return $trim ? trim( $value ) : $value;
109
+ }
110
+
111
+
112
+
113
+ /**
114
+ * Returns integer value
115
+ *
116
+ * @param string $key
117
+ * @param integer $default
118
+ * @return integer
119
+ */
120
+ public function get_integer( $key, $default = 0 ) {
121
+ return (integer)$this->get( $key, $default );
122
+ }
123
+
124
+
125
+
126
+ /**
127
+ * Returns boolean value
128
+ *
129
+ * @param string $key
130
+ * @param boolean $default
131
+ * @return boolean
132
+ */
133
+ public function get_boolean( $key, $default = false ) {
134
+ $v = $this->get( $key, $default );
135
+ if ( $v === 'false' || $v === 0 )
136
+ $v = false;
137
+
138
+ return (boolean)$v;
139
+ }
140
+
141
+
142
+
143
+ /**
144
+ * Returns array value
145
+ *
146
+ * @param string $key
147
+ * @param array $default
148
+ * @return array
149
+ */
150
+ public function get_array( $key, $default = array() ) {
151
+ return (array)$this->get( $key, $default );
152
+ }
153
+
154
+
155
+
156
+ /**
157
+ * Sets config value
158
+ *
159
+ * @param string $key
160
+ * @param string $value
161
+ * @return value set
162
+ */
163
+ public function set( $key, $value ) {
164
+ $this->_data[$key] = $value;
165
+ }
166
+
167
+
168
+
169
+ public function reset() {
170
+ $this->_data = array();
171
+ $this->apply_defaults();
172
+ }
173
+
174
+
175
+
176
+ /**
177
+ * Saves modified config
178
+ */
179
+ public function save() {
180
+ if ( $this->_is_master )
181
+ update_site_option( 'w3tc_state', json_encode( $this->_data ) );
182
+ else
183
+ update_option( 'w3tc_state', json_encode( $this->_data ) );
184
+ }
185
+
186
+
187
+
188
+ private function apply_defaults() {
189
+ $this->set( 'common.install', time() );
190
+ }
191
+ }
ConfigStateNote.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Used to show notes at blog-level when master configs are changed
6
+ *
7
+ * keys - see ConfigState comment with a list of keys with "timestamp" word
8
+ */
9
+ class ConfigStateNote {
10
+ private $_config_state_master;
11
+ private $_config_state;
12
+
13
+
14
+
15
+ /**
16
+ * Constructor
17
+ */
18
+ public function __construct( $config_state_master, $config_state ) {
19
+ $this->_config_state_master = $config_state_master;
20
+ $this->_config_state = $config_state;
21
+ }
22
+
23
+ /**
24
+ * Returns value
25
+ *
26
+ * @param string $key
27
+ * @param string $default
28
+ * @return mixed
29
+ */
30
+ public function get( $key ) {
31
+ $timestamp = $this->_config_state->get_integer( $key . '.timestamp' );
32
+ $timestamp_master = $this->_config_state_master->get_integer(
33
+ $key . '.timestamp' );
34
+
35
+ if ( $timestamp > $timestamp_master )
36
+ return $this->_config_state->get_boolean( $key );
37
+ else
38
+ return $this->_config_state_master->get_boolean( $key );
39
+ }
40
+
41
+ /**
42
+ * Sets flag to true/false
43
+ */
44
+ public function set( $key, $value ) {
45
+ $this->_config_state->set( $key, $value );
46
+ $this->_config_state->set( $key . '.timestamp', time() );
47
+ $this->_config_state->save();
48
+ }
49
+ }
DbCache_ConfigLabels.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class DbCache_ConfigLabels {
5
+ public function config_labels( $config_labels ) {
6
+ return array_merge( $config_labels, array(
7
+ 'dbcache.engine' => __( 'Database Cache Method:', 'w3-total-cache' ),
8
+ 'dbcache.enabled' => __( 'Database Cache:', 'w3-total-cache' ),
9
+ 'dbcache.debug' => __( 'Database Cache', 'w3-total-cache' ),
10
+ 'dbcache.reject.logged' => __( 'Don\'t cache queries for logged in users', 'w3-total-cache' ),
11
+ 'dbcache.lifetime' => __( 'Maximum lifetime of cache objects:', 'w3-total-cache' ),
12
+ 'dbcache.file.gc' => __( 'Garbage collection interval:', 'w3-total-cache' ),
13
+ 'dbcache.reject.uri' => __( 'Never cache the following pages:', 'w3-total-cache' ),
14
+ 'dbcache.reject.sql' => __( 'Ignored query stems:', 'w3-total-cache' ),
15
+ 'dbcache.reject.words' => __( 'Reject query words:', 'w3-total-cache' )
16
+ ) );
17
+ }
18
+ }
DbCache_Core.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * component of shared code used by dbcache
6
+ */
7
+ class DbCache_Core {
8
+ public function get_usage_statistics_cache_config() {
9
+ $c = Dispatcher::config();
10
+ $engine = $c->get_string( 'dbcache.engine' );
11
+
12
+ switch ( $engine ) {
13
+ case 'memcached':
14
+ $engineConfig = array(
15
+ 'servers' => $c->get_array( 'dbcache.memcached.servers' ),
16
+ 'persistent' => $c->get_boolean( 'dbcache.memcached.persistent' ),
17
+ 'aws_autodiscovery' => $c->get_boolean( 'dbcache.memcached.aws_autodiscovery' ),
18
+ 'username' => $c->get_boolean( 'dbcache.memcached.username' ),
19
+ 'password' => $c->get_boolean( 'dbcache.memcached.password' )
20
+ );
21
+ break;
22
+
23
+ case 'redis':
24
+ $engineConfig = array(
25
+ 'servers' => $c->get_array( 'dbcache.redis.servers' ),
26
+ 'persistent' => $c->get_boolean( 'dbcache.redis.persistent' ),
27
+ 'dbid' => $c->get_boolean( 'dbcache.redis.dbid' ),
28
+ 'password' => $c->get_boolean( 'dbcache.redis.password' )
29
+ );
30
+ break;
31
+
32
+ default:
33
+ $engineConfig = array();
34
+ }
35
+
36
+ $engineConfig['engine'] = $engine;
37
+ return $engineConfig;
38
+ }
39
+ }
DbCache_Environment.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 PgCache plugin - administrative interface
6
+ */
7
+
8
+ /**
9
+ * class DbCache_Environment
10
+ */
11
+ class DbCache_Environment {
12
+ /**
13
+ * Fixes environment in each wp-admin request
14
+ *
15
+ * @param Config $config
16
+ * @param bool $force_all_checks
17
+ *
18
+ * @throws Util_Environment_Exceptions
19
+ */
20
+ public function fix_on_wpadmin_request( $config, $force_all_checks ) {
21
+ $exs = new Util_Environment_Exceptions();
22
+ try {
23
+ if ( $config->get_boolean( 'dbcache.enabled' ) )
24
+ $this->create_addin();
25
+ else
26
+ $this->delete_addin();
27
+ } catch ( Util_WpFile_FilesystemOperationException $ex ) {
28
+ $exs->push( $ex );
29
+ }
30
+
31
+ if ( count( $exs->exceptions() ) > 0 )
32
+ throw $exs;
33
+ }
34
+
35
+ /**
36
+ * Fixes environment once event occurs
37
+ *
38
+ * @throws Util_Environment_Exceptions
39
+ */
40
+ public function fix_on_event( $config, $event, $old_config = null ) {
41
+ if ( $config->get_boolean( 'dbcache.enabled' ) &&
42
+ $config->get_string( 'dbcache.engine' ) == 'file' ) {
43
+ if ( !wp_next_scheduled( 'w3_dbcache_cleanup' ) ) {
44
+ wp_schedule_event( time(),
45
+ 'w3_dbcache_cleanup', 'w3_dbcache_cleanup' );
46
+ }
47
+ } else {
48
+ $this->unschedule();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Fixes environment after plugin deactivation
54
+ *
55
+ * @throws Util_Environment_Exceptions
56
+ * @return array
57
+ */
58
+ public function fix_after_deactivation() {
59
+ $exs = new Util_Environment_Exceptions();
60
+
61
+ try {
62
+ $this->delete_addin();
63
+ } catch ( Util_WpFile_FilesystemOperationException $ex ) {
64
+ $exs->push( $ex );
65
+ }
66
+
67
+ $this->unschedule();
68
+
69
+ if ( count( $exs->exceptions() ) > 0 )
70
+ throw $exs;
71
+ }
72
+
73
+ /**
74
+ * Returns required rules for module
75
+ *
76
+ * @var Config $config
77
+ * @return array
78
+ */
79
+ function get_required_rules( $config ) {
80
+ return null;
81
+ }
82
+
83
+ /**
84
+ * scheduling stuff
85
+ */
86
+ private function unschedule() {
87
+ if ( wp_next_scheduled( 'w3_dbcache_cleanup' ) ) {
88
+ wp_clear_scheduled_hook( 'w3_dbcache_cleanup' );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Creates add-in
94
+ *
95
+ * @throws Util_WpFile_FilesystemOperationException
96
+ */
97
+ private function create_addin() {
98
+ $src = W3TC_INSTALL_FILE_DB;
99
+ $dst = W3TC_ADDIN_FILE_DB;
100
+
101
+
102
+ if ( $this->db_installed() ) {
103
+ if ( $this->is_dbcache_add_in() ) {
104
+ $script_data = @file_get_contents( $dst );
105
+ if ( $script_data == @file_get_contents( $src ) )
106
+ return;
107
+ } else if ( get_transient( 'w3tc_remove_add_in_dbcache' ) == 'yes' ) {
108
+ // user already manually asked to remove another plugin's add in,
109
+ // we should try to apply ours
110
+ // (in case of missing permissions deletion could fail)
111
+ } else if ( !$this->db_check_old_add_in() ) {
112
+
113
+
114
+ if ( isset( $_GET['page'] ) )
115
+ $url = 'admin.php?page=' . $_GET['page'] . '&amp;';
116
+ else
117
+ $url = basename( Util_Environment::remove_query( $_SERVER['REQUEST_URI'] ) ) . '?page=w3tc_dashboard&amp;';
118
+ $remove_url = Util_Ui::admin_url( $url . 'w3tc_default_remove_add_in=dbcache' );
119
+ throw new Util_WpFile_FilesystemOperationException(
120
+ sprintf( __( 'The Database add-in file db.php is not a W3 Total Cache drop-in.
121
+ Remove it or disable Database Caching. %s', 'w3-total-cache' ),
122
+ Util_Ui::button_link( __( 'Remove it for me', 'w3-total-cache' ), wp_nonce_url( $remove_url, 'w3tc' ) ) ) );
123
+ }
124
+ }
125
+
126
+ Util_WpFile::copy_file( $src, $dst );
127
+ }
128
+
129
+ /**
130
+ * Deletes add-in
131
+ *
132
+ * @throws Util_WpFile_FilesystemOperationException
133
+ */
134
+ private function delete_addin() {
135
+ if ( $this->is_dbcache_add_in() )
136
+ Util_WpFile::delete_file( W3TC_ADDIN_FILE_DB );
137
+ }
138
+
139
+ /**
140
+ * Returns true if db.php is installed
141
+ *
142
+ * @return boolean
143
+ */
144
+ public function db_installed() {
145
+ return file_exists( W3TC_ADDIN_FILE_DB );
146
+ }
147
+
148
+ /**
149
+ * Returns true if db.php is old version.
150
+ *
151
+ * @return boolean
152
+ */
153
+ public function db_check_old_add_in() {
154
+ return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_DB ) )
155
+ && strstr( $script_data, 'w3_instance' ) !== false );
156
+ }
157
+
158
+ /**
159
+ * Checks if db.php is W3TC drop in
160
+ *
161
+ * @return boolean
162
+ */
163
+ public function is_dbcache_add_in() {
164
+ return ( ( $script_data = @file_get_contents( W3TC_ADDIN_FILE_DB ) )
165
+ && strstr( $script_data, 'DbCache_Wpdb' ) !== false );
166
+ }
167
+ }
DbCache_Page.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class DbCache_Page extends Base_Page_Settings {
7
+ /**
8
+ * Current page
9
+ *
10
+ * @var string
11
+ */
12
+ protected $_page = 'w3tc_dbcache';
13
+
14
+
15
+ /**
16
+ * Database cache tab
17
+ *
18
+ * @return void
19
+ */
20
+ function view() {
21
+ $dbcache_enabled = $this->_config->get_boolean( 'dbcache.enabled' );
22
+
23
+ include W3TC_INC_DIR . '/options/dbcache.php';
24
+ }
25
+
26
+ /**
27
+ * Database cluster config editor
28
+ *
29
+ * @return void
30
+ */
31
+ function dbcluster_config() {
32
+ $this->_page = 'w3tc_dbcluster_config';
33
+ if ( Util_Environment::is_dbcluster() )
34
+ $content = @file_get_contents( W3TC_FILE_DB_CLUSTER_CONFIG );
35
+ else
36
+ $content = @file_get_contents( W3TC_DIR . '/ini/dbcluster-config-sample.php' );
37
+
38
+ include W3TC_INC_OPTIONS_DIR . '/enterprise/dbcluster-config.php';
39
+ }
40
+ }
DbCache_Plugin.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 DbCache plugin
6
+ */
7
+ class DbCache_Plugin {
8
+ /**
9
+ * Config
10
+ */
11
+ private $_config = null;
12
+
13
+ function __construct() {
14
+ $this->_config = Dispatcher::config();
15
+ }
16
+
17
+ /**
18
+ * Runs plugin
19
+ */
20
+ function run() {
21
+ add_filter( 'cron_schedules', array(
22
+ $this,
23
+ 'cron_schedules'
24
+ ) );
25
+
26
+ if ( $this->_config->get_string( 'dbcache.engine' ) == 'file' ) {
27
+ add_action( 'w3_dbcache_cleanup', array(
28
+ $this,
29
+ 'cleanup'
30
+ ) );
31
+ }
32
+
33
+ add_action( 'publish_phone', array(
34
+ $this,
35
+ 'on_change'
36
+ ), 0 );
37
+
38
+ add_action( 'wp_trash_post', array(
39
+ $this,
40
+ 'on_post_change'
41
+ ), 0 );
42
+
43
+ add_action( 'save_post', array(
44
+ $this,
45
+ 'on_post_change'
46
+ ), 0 );
47
+
48
+ add_action( 'clean_post_cache', array(
49
+ $this,
50
+ 'on_post_change'
51
+ ), 0, 2 );
52
+
53
+ add_action( 'comment_post', array(
54
+ $this,
55
+ 'on_comment_change'
56
+ ), 0 );
57
+
58
+ add_action( 'edit_comment', array(
59
+ $this,
60
+ 'on_comment_change'
61
+ ), 0 );
62
+
63
+ add_action( 'delete_comment', array(
64
+ $this,
65
+ 'on_comment_change'
66
+ ), 0 );
67
+
68
+ add_action( 'wp_set_comment_status', array(
69
+ $this,
70
+ 'on_comment_status'
71
+ ), 0, 2 );
72
+
73
+ add_action( 'trackback_post', array(
74
+ $this,
75
+ 'on_comment_change'
76
+ ), 0 );
77
+
78
+ add_action( 'pingback_post', array(
79
+ $this,
80
+ 'on_comment_change'
81
+ ), 0 );
82
+
83
+ add_action( 'switch_theme', array(
84
+ $this,
85
+ 'on_change'
86
+ ), 0 );
87
+
88
+ add_action( 'edit_user_profile_update', array(
89
+ $this,
90
+ 'on_change'
91
+ ), 0 );
92
+
93
+ if ( Util_Environment::is_wpmu() ) {
94
+ add_action( 'delete_blog', array(
95
+ $this,
96
+ 'on_change'
97
+ ), 0 );
98
+ }
99
+
100
+ add_action( 'delete_post', array(
101
+ $this,
102
+ 'on_post_change'
103
+ ), 0 );
104
+
105
+ add_filter( 'w3tc_admin_bar_menu',
106
+ array( $this, 'w3tc_admin_bar_menu' ) );
107
+
108
+ // usage statistics handling
109
+ add_filter( 'w3tc_usage_statistics_metrics', array(
110
+ $this, 'w3tc_usage_statistics_metrics' ) );
111
+ }
112
+
113
+ /**
114
+ * Does disk cache cleanup
115
+ *
116
+ * @return void
117
+ */
118
+ function cleanup() {
119
+ $w3_cache_file_cleaner = new Cache_File_Cleaner( array(
120
+ 'cache_dir' => Util_Environment::cache_blog_dir( 'db' ),
121
+ 'clean_timelimit' => $this->_config->get_integer( 'timelimit.cache_gc' )
122
+ ) );
123
+
124
+ $w3_cache_file_cleaner->clean();
125
+ }
126
+
127
+ /**
128
+ * Cron schedules filter
129
+ *
130
+ * @param array $schedules
131
+ * @return array
132
+ */
133
+ function cron_schedules( $schedules ) {
134
+ $gc = $this->_config->get_integer( 'dbcache.file.gc' );
135
+
136
+ return array_merge( $schedules, array(
137
+ 'w3_dbcache_cleanup' => array(
138
+ 'interval' => $gc,
139
+ 'display' => sprintf( '[W3TC] Database Cache file GC (every %d seconds)', $gc )
140
+ )
141
+ ) );
142
+ }
143
+
144
+ /**
145
+ * Change action
146
+ */
147
+ function on_change() {
148
+ static $flushed = false;
149
+
150
+ if ( !$flushed ) {
151
+ $flusher = Dispatcher::component( 'CacheFlush' );
152
+ $flusher->dbcache_flush();
153
+
154
+ $flushed = true;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Change post action
160
+ */
161
+ function on_post_change( $post_id = 0, $post = null ) {
162
+ static $flushed = false;
163
+
164
+ if ( !$flushed ) {
165
+ if ( is_null( $post ) )
166
+ $post = $post_id;
167
+
168
+ if ( $post_id>0 && !Util_Environment::is_flushable_post( $post, 'dbcache', $this->_config ) ) {
169
+ return;
170
+ }
171
+
172
+ $flusher = Dispatcher::component( 'CacheFlush' );
173
+ $flusher->dbcache_flush();
174
+
175
+ $flushed = true;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Comment change action
181
+ *
182
+ * @param integer $comment_id
183
+ */
184
+ function on_comment_change( $comment_id ) {
185
+ $post_id = 0;
186
+
187
+ if ( $comment_id ) {
188
+ $comment = get_comment( $comment_id, ARRAY_A );
189
+ $post_id = !empty( $comment['comment_post_ID'] ) ? (int) $comment['comment_post_ID'] : 0;
190
+ }
191
+
192
+ $this->on_post_change( $post_id );
193
+ }
194
+
195
+ /**
196
+ * Comment status action
197
+ *
198
+ * @param integer $comment_id
199
+ * @param string $status
200
+ */
201
+ function on_comment_status( $comment_id, $status ) {
202
+ if ( $status === 'approve' || $status === '1' ) {
203
+ $this->on_comment_change( $comment_id );
204
+ }
205
+ }
206
+
207
+
208
+
209
+ public function w3tc_admin_bar_menu( $menu_items ) {
210
+ $menu_items['20310.dbcache'] = array(
211
+ 'id' => 'w3tc_flush_dbcache',
212
+ 'parent' => 'w3tc_flush',
213
+ 'title' => __( 'Database', 'w3-total-cache' ),
214
+ 'href' => wp_nonce_url( network_admin_url(
215
+ 'admin.php?page=w3tc_dashboard&amp;w3tc_flush_dbcache' ),
216
+ 'w3tc' )
217
+ );
218
+
219
+ return $menu_items;
220
+ }
221
+
222
+ public function w3tc_usage_statistics_of_request( $storage ) {
223
+ $o = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
224
+ $o->w3tc_usage_statistics_of_request( $storage );
225
+ }
226
+
227
+ public function w3tc_usage_statistics_metrics( $metrics ) {
228
+ return array_merge( $metrics, array(
229
+ 'dbcache_calls_total', 'dbcache_calls_hits' ) );
230
+ }
231
+ }
DbCache_Plugin_Admin.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class DbCache_Plugin_Admin {
5
+ function run() {
6
+ $config_labels = new DbCache_ConfigLabels();
7
+ add_filter( 'w3tc_config_labels', array( $config_labels, 'config_labels' ) );
8
+
9
+ $c = Dispatcher::config();
10
+ if ( $c->get_boolean( 'dbcache.enabled' ) ) {
11
+ add_filter( 'w3tc_usage_statistics_summary_from_history', array(
12
+ $this, 'w3tc_usage_statistics_summary_from_history' ), 10, 2 );
13
+ add_filter( 'w3tc_errors', array( $this, 'w3tc_errors' ) );
14
+ }
15
+ }
16
+
17
+
18
+
19
+ public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
20
+ // memcached servers
21
+ $c = Dispatcher::config();
22
+ if ( $c->get_string( 'dbcache.engine' ) == 'memcached' ) {
23
+ $summary['memcached_servers']['dbcache'] = array(
24
+ 'servers' => $c->get_array( 'dbcache.memcached.servers' ),
25
+ 'username' => $c->get_boolean( 'dbcache.memcached.username' ),
26
+ 'password' => $c->get_boolean( 'dbcache.memcached.password' ),
27
+ 'name' => __( 'Database Cache', 'w3-total-cache' )
28
+ );
29
+ } elseif ( $c->get_string( 'dbcache.engine' ) == 'redis' ) {
30
+ $summary['redis_servers']['dbcache'] = array(
31
+ 'servers' => $c->get_array( 'dbcache.redis.servers' ),
32
+ 'username' => $c->get_boolean( 'dbcache.redis.username' ),
33
+ 'dbid' => $c->get_boolean( 'dbcache.redis.dbid' ),
34
+ 'password' => $c->get_boolean( 'dbcache.redis.password' ),
35
+ 'name' => __( 'Database Cache', 'w3-total-cache' )
36
+ );
37
+ }
38
+
39
+
40
+ // counters
41
+ $dbcache_calls_total = Util_UsageStatistics::sum( $history,
42
+ 'dbcache_calls_total' );
43
+ $dbcache_calls_hits = Util_UsageStatistics::sum( $history,
44
+ 'dbcache_calls_hits' );
45
+
46
+ $summary['dbcache'] = array(
47
+ 'calls_total' => Util_UsageStatistics::integer(
48
+ $dbcache_calls_total ),
49
+ 'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
50
+ $dbcache_calls_total, $summary ),
51
+ 'hit_rate' => Util_UsageStatistics::percent(
52
+ $dbcache_calls_total, $dbcache_calls_total )
53
+ );
54
+
55
+ return $summary;
56
+ }
57
+
58
+
59
+
60
+ public function w3tc_errors( $errors ) {
61
+ $c = Dispatcher::config();
62
+
63
+ if ( $c->get_string( 'dbcache.engine' ) == 'memcached' ) {
64
+ $memcached_servers = $c->get_array( 'dbcache.memcached.servers' );
65
+
66
+ if ( !Util_Installed::is_memcache_available( $memcached_servers ) ) {
67
+ if ( !isset( $errors['memcache_not_responding.details'] ) )
68
+ $errors['memcache_not_responding.details'] = array();
69
+
70
+ $errors['memcache_not_responding.details'][] = sprintf(
71
+ __( 'Database Cache: %s.', 'w3-total-cache' ),
72
+ implode( ', ', $memcached_servers ) );
73
+ }
74
+ }
75
+
76
+ return $errors;
77
+ }
78
+ }
DbCache_Wpdb.php ADDED
@@ -0,0 +1,464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class Db
6
+ * Database access mediator
7
+ */
8
+ class DbCache_Wpdb extends DbCache_WpdbBase {
9
+ /**
10
+ * Returns object instance. Called by WP engine
11
+ *
12
+ * @return DbCache_Wpdb
13
+ */
14
+ static function instance() {
15
+ static $instance = null;
16
+
17
+ if ( is_null( $instance ) ) {
18
+ $processors = array();
19
+ $call_default_constructor = true;
20
+
21
+ // no caching during activation
22
+ $is_installing = ( defined( 'WP_INSTALLING' ) && WP_INSTALLING );
23
+
24
+ $config = Dispatcher::config();
25
+ if ( !$is_installing && $config->get_boolean( 'dbcache.enabled' ) ) {
26
+ $processors[] = new DbCache_WpdbInjection_QueryCaching();
27
+ }
28
+ if ( Util_Environment::is_dbcluster() ) {
29
+ $processors[] = new Enterprise_Dbcache_WpdbInjection_Cluster();
30
+ }
31
+
32
+ $processors[] = new DbCache_WpdbInjection();
33
+
34
+ $class = __CLASS__;
35
+ $o = new $class( $processors );
36
+
37
+ $next_injection = new _CallUnderlying( $o );
38
+
39
+ foreach ( $processors as $processor ) {
40
+ $processor->initialize_injection( $o, $next_injection );
41
+ }
42
+
43
+ // initialize after processors configured
44
+ $o->initialize();
45
+
46
+ $instance = $o;
47
+ }
48
+
49
+ return $instance;
50
+ }
51
+
52
+ private $processor_number;
53
+ private $active_processor;
54
+ private $active_processors;
55
+
56
+ private $debug;
57
+ private $request_time_start = 0;
58
+
59
+ /*
60
+ * @param boolean $call_default_constructor
61
+ */
62
+ public function __construct( $processors = null ) {
63
+ // cant force empty parameter list due to wp requirements
64
+ if ( !is_array( $processors ) )
65
+ throw new Exception( 'called incorrectly, use instance()' );
66
+
67
+ $this->processors = $processors;
68
+ $this->active_processor = $processors[0];
69
+ $this->active_processor_number = 0;
70
+
71
+ $c = Dispatcher::config();
72
+ $this->debug = $c->get_boolean( 'dbcache.debug' );
73
+
74
+ if ( $this->debug )
75
+ $this->_request_time_start = microtime( true );
76
+ }
77
+
78
+ /**
79
+ * Called by Root_Loader when all w3tc plugins loaded,
80
+ * i.e. later that object instantiated
81
+ */
82
+ function on_w3tc_plugins_loaded() {
83
+ $o = $this;
84
+
85
+ if ( $this->debug ) {
86
+ add_action( 'shutdown', array( $o, 'debug_shutdown' ) );
87
+ }
88
+
89
+ add_filter( 'w3tc_footer_comment', array(
90
+ $o, 'w3tc_footer_comment' ) );
91
+ add_action( 'w3tc_usage_statistics_of_request', array(
92
+ $o, 'w3tc_usage_statistics_of_request' ), 10, 1 );
93
+
94
+ }
95
+
96
+ function w3tc_footer_comment( $strings ) {
97
+ foreach ( $this->processors as $processor )
98
+ $strings = $processor->w3tc_footer_comment( $strings );
99
+
100
+ return $strings;
101
+ }
102
+
103
+ function debug_shutdown() {
104
+ $strings = array();
105
+ foreach ( $this->processors as $processor )
106
+ $strings = $processor->w3tc_footer_comment( $strings );
107
+
108
+ $request_time_total = microtime( true ) - $this->request_time_start;
109
+
110
+ $data = sprintf( "\n[%s] [%s] [%s]\n", date( 'r' ),
111
+ $_SERVER['REQUEST_URI'], round( $request_time_total, 4 ) ) .
112
+ implode( "\n", $strings ) . "\n";
113
+ $data = strtr( $data, '<>', '..' );
114
+
115
+ $filename = Util_Debug::log_filename( 'dbcache' );
116
+ @file_put_contents( $filename, $data, FILE_APPEND );
117
+ }
118
+
119
+ function w3tc_usage_statistics_of_request( $storage ) {
120
+ foreach ( $this->processors as $processor )
121
+ $processor->w3tc_usage_statistics_of_request( $storage );
122
+ }
123
+
124
+ function flush_cache() {
125
+ $v = true;
126
+
127
+ foreach ( $this->processors as $processor )
128
+ $v &= $processor->flush_cache();
129
+
130
+ return $v;
131
+ }
132
+
133
+ /**
134
+ * Initializes object after processors configured. Called from instance() only
135
+ */
136
+ function initialize() {
137
+ return $this->active_processor->initialize();
138
+ }
139
+
140
+ /**
141
+ * Overriten logic of wp_db by processor.
142
+ */
143
+ function insert( $table, $data, $format = null ) {
144
+ do_action( 'w3tc_db_insert', $table, $data, $format );
145
+ return $this->active_processor->insert( $table, $data, $format );
146
+ }
147
+
148
+ /**
149
+ * Overriten logic of wp_db by processor.
150
+ */
151
+ function query( $query ) {
152
+ return $this->active_processor->query( $query );
153
+ }
154
+
155
+ /**
156
+ * Overriten logic of wp_db by processor.
157
+ */
158
+ function replace( $table, $data, $format = null ) {
159
+ do_action( 'w3tc_db_replace', $table, $data, $format );
160
+ return $this->active_processor->replace( $table, $data, $format );
161
+ }
162
+
163
+ /**
164
+ * Overriten logic of wp_db by processor.
165
+ */
166
+ function update( $table, $data, $where, $format = null, $where_format = null ) {
167
+ do_action( 'w3tc_db_update', $table, $data, $where, $format,
168
+ $where_format );
169
+ return $this->active_processor->update( $table, $data, $where, $format, $where_format );
170
+ }
171
+
172
+ /**
173
+ * Overriten logic of wp_db by processor.
174
+ */
175
+ function delete( $table, $where, $where_format = null ) {
176
+ do_action( 'w3tc_db_delete', $table, $where, $where_format );
177
+ return $this->active_processor->delete( $table, $where, $where_format );
178
+ }
179
+
180
+ /**
181
+ * Overriten logic of wp_db by processor.
182
+ */
183
+ function init_charset() {
184
+ return $this->active_processor->init_charset();
185
+ }
186
+
187
+ /**
188
+ * Overriten logic of wp_db by processor.
189
+ */
190
+ function set_charset( $dbh, $charset = null, $collate = null ) {
191
+ return $this->active_processor->set_charset( $dbh, $charset, $collate );
192
+ }
193
+
194
+ /**
195
+ * Overriten logic of wp_db by processor.
196
+ */
197
+ function flush() {
198
+ return $this->active_processor->flush();
199
+ }
200
+
201
+ /**
202
+ * Overriten logic of wp_db by processor.
203
+ */
204
+ function check_database_version( $dbh_or_table = false ) {
205
+ return $this->active_processor->check_database_version( $dbh_or_table );
206
+ }
207
+
208
+ /**
209
+ * Overriten logic of wp_db by processor.
210
+ */
211
+ function supports_collation( $dbh_or_table = false ) {
212
+ return $this->active_processor->supports_collation( $dbh_or_table );
213
+ }
214
+
215
+ /**
216
+ * Overriten logic of wp_db by processor.
217
+ */
218
+ function has_cap( $db_cap, $dbh_or_table = false ) {
219
+ return $this->active_processor->has_cap( $db_cap, $dbh_or_table );
220
+ }
221
+
222
+ /**
223
+ * Overriten logic of wp_db by processor.
224
+ */
225
+ function db_version( $dbh_or_table = false ) {
226
+ return $this->active_processor->db_version( $dbh_or_table );
227
+ }
228
+
229
+ /**
230
+ * Default initialization method, calls wp_db apropriate method
231
+ */
232
+ function default_initialize() {
233
+ parent::__construct( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
234
+ }
235
+
236
+ /**
237
+ * Default implementation, calls wp_db apropriate method
238
+ */
239
+ function default_insert( $table, $data, $format = null ) {
240
+ return parent::insert( $table, $data, $format );
241
+ }
242
+
243
+ /**
244
+ * Default implementation, calls wp_db apropriate method
245
+ */
246
+ function default_query( $query ) {
247
+ return parent::query( $query );
248
+ }
249
+
250
+ /**
251
+ * Default implementation, calls wp_db apropriate method
252
+ */
253
+ function default_replace( $table, $data, $format = null ) {
254
+ return parent::replace( $table, $data, $format );
255
+ }
256
+
257
+ /**
258
+ * Default implementation, calls wp_db apropriate method
259
+ */
260
+ function default_update( $table, $data, $where, $format = null, $where_format = null ) {
261
+ return parent::update( $table, $data, $where, $format, $where_format );
262
+ }
263
+
264
+ /**
265
+ * Default implementation, calls wp_db apropriate method
266
+ */
267
+ function default_delete( $table, $where, $where_format = null ) {
268
+ return parent::delete( $table, $where, $where_format );
269
+ }
270
+
271
+ /**
272
+ * Default implementation, calls wp_db apropriate method
273
+ */
274
+ function default_init_charset() {
275
+ return parent::init_charset();
276
+ }
277
+
278
+ /**
279
+ * Default implementation, calls wp_db apropriate method
280
+ */
281
+ function default_set_charset( $dbh, $charset = null, $collate = null ) {
282
+ return parent::set_charset( $dbh, $charset, $collate );
283
+ }
284
+
285
+ /**
286
+ * Default implementation, calls wp_db apropriate method
287
+ */
288
+ function default_flush() {
289
+ return parent::flush();
290
+ }
291
+
292
+ /**
293
+ * Default implementation, calls wp_db apropriate method
294
+ */
295
+ function default_check_database_version( $dbh_or_table = false ) {
296
+ return parent::check_database_version( $dbh_or_table );
297
+ }
298
+
299
+ /**
300
+ * Default implementation, calls wp_db apropriate method
301
+ */
302
+ function default_supports_collation( $dbh_or_table = false ) {
303
+ return parent::supports_collation( $dbh_or_table );
304
+ }
305
+
306
+ /**
307
+ * Default implementation, calls wp_db apropriate method
308
+ */
309
+ function default_has_cap( $db_cap, $dbh_or_table = false ) {
310
+ return parent::has_cap( $db_cap, $dbh_or_table );
311
+ }
312
+
313
+ /**
314
+ * Default implementation, calls wp_db apropriate method
315
+ */
316
+ function default_db_version( $dbh_or_table = false ) {
317
+ return parent::db_version( $dbh_or_table );
318
+ }
319
+
320
+ /**
321
+ * Default implementation, calls wp_db apropriate method
322
+ */
323
+ function switch_active_processor( $offset ) {
324
+ $new_processor_number = $this->active_processor_number + $offset;
325
+ if ( $new_processor_number <= 0 ) {
326
+ $new_processor_number = 0;
327
+ } else if ( $new_processor_number >= count( $this->processors ) ) {
328
+ $new_processor_number = count( $this->processors ) - 1;
329
+ }
330
+
331
+ $offset_made = $new_processor_number - $this->active_processor_number;
332
+ $this->active_processor_number = $new_processor_number;
333
+ $this->active_processor = $this->processors[$new_processor_number];
334
+
335
+ return $offset_made;
336
+ }
337
+ }
338
+
339
+
340
+
341
+ /**
342
+ * class CallUnderlying
343
+ */
344
+ class _CallUnderlying {
345
+ function __construct( $manager ) {
346
+ $this->wpdb_mixin = $manager;
347
+ }
348
+
349
+ /**
350
+ * Calls underlying processor's aproptiate method of wp_db
351
+ */
352
+ function initialize() {
353
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
354
+
355
+ try {
356
+ $r = $this->wpdb_mixin->initialize();
357
+
358
+ $this->wpdb_mixin->switch_active_processor( -$switched );
359
+ return $r;
360
+ } catch ( \Exception $e ) {
361
+ $this->wpdb_mixin->switch_active_processor( -$switched );
362
+ throw $e;
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Calls underlying processor's aproptiate method of wp_db
368
+ */
369
+ function flush() {
370
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
371
+
372
+ try {
373
+ $r = $this->wpdb_mixin->flush();
374
+
375
+ $this->wpdb_mixin->switch_active_processor( -$switched );
376
+ return $r;
377
+ } catch ( \Exception $e ) {
378
+ $this->wpdb_mixin->switch_active_processor( -$switched );
379
+ throw $e;
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Calls underlying processor's aproptiate method of wp_db
385
+ */
386
+ function query( $query ) {
387
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
388
+
389
+ try {
390
+ $r = $this->wpdb_mixin->query( $query );
391
+
392
+ $this->wpdb_mixin->switch_active_processor( -$switched );
393
+ return $r;
394
+ } catch ( \Exception $e ) {
395
+ $this->wpdb_mixin->switch_active_processor( -$switched );
396
+ throw $e;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Calls underlying processor's aproptiate method of wp_db
402
+ */
403
+ function insert( $table, $data, $format = null ) {
404
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
405
+
406
+ try {
407
+ $r = $this->wpdb_mixin->insert( $table, $data, $format );
408
+
409
+ $this->wpdb_mixin->switch_active_processor( -$switched );
410
+ return $r;
411
+ } catch ( \Exception $e ) {
412
+ $this->wpdb_mixin->switch_active_processor( -$switched );
413
+ throw $e;
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Calls underlying processor's aproptiate method of wp_db
419
+ */
420
+ function replace( $table, $data, $format = null ) {
421
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
422
+
423
+ try {
424
+ $r = $this->wpdb_mixin->replace( $table, $data, $format );
425
+
426
+ $this->wpdb_mixin->switch_active_processor( -$switched );
427
+ return $r;
428
+ } catch ( \Exception $e ) {
429
+ $this->wpdb_mixin->switch_active_processor( -$switched );
430
+ throw $e;
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Calls underlying processor's aproptiate method of wp_db
436
+ */
437
+ function update( $table, $data, $where, $format = null, $where_format = null ) {
438
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
439
+
440
+ try {
441
+ $r = $this->wpdb_mixin->update( $table, $data, $where, $format, $where_format );
442
+
443
+ $this->wpdb_mixin->switch_active_processor( -$switched );
444
+ return $r;
445
+ } catch ( \Exception $e ) {
446
+ $this->wpdb_mixin->switch_active_processor( -$switched );
447
+ throw $e;
448
+ }
449
+ }
450
+
451
+ function delete( $table, $where, $where_format = null ) {
452
+ $switched = $this->wpdb_mixin->switch_active_processor( 1 );
453
+
454
+ try {
455
+ $r = $this->wpdb_mixin->delete( $table, $where, $where_format );
456
+
457
+ $this->wpdb_mixin->switch_active_processor( -$switched );
458
+ return $r;
459
+ } catch ( \Exception $e ) {
460
+ $this->wpdb_mixin->switch_active_processor( -$switched );
461
+ throw $e;
462
+ }
463
+ }
464
+ }
DbCache_WpdbBase.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ require_once ABSPATH . 'wp-includes/wp-db.php';
5
+
6
+ class DbCache_WpdbBase extends \wpdb {
7
+ }
DbCache_WpdbInjection.php ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class DbCache_WpdbInjection
6
+ * Allows to perform own operation instead of default behaviour of wpdb
7
+ * without inheritance
8
+ */
9
+ class DbCache_WpdbInjection {
10
+ /**
11
+ * Top database-connection object.
12
+ * Initialized by DbCache_Wpdb::instance
13
+ *
14
+ * @var object
15
+ */
16
+ protected $wpdb_mixin = null;
17
+
18
+ /**
19
+ * Database-connection using overrides of next processor in queue
20
+ * Initialized by DbCache_Wpdb::instance
21
+ *
22
+ * @var object
23
+ */
24
+ protected $next_injection = null;
25
+
26
+ /**
27
+ * initialization of object so that it can be used
28
+ */
29
+ function initialize_injection( $wpdb_mixin, $next_injection ) {
30
+ $this->wpdb_mixin = $wpdb_mixin;
31
+ $this->next_injection = $next_injection;
32
+ }
33
+
34
+ /**
35
+ * Placeholder for database initialization
36
+ */
37
+ function initialize() {
38
+ return $this->wpdb_mixin->default_initialize();
39
+ }
40
+
41
+ /**
42
+ * Placeholder for apropriate wp_db method replacement.
43
+ * By default calls wp_db implementation
44
+ */
45
+ function insert( $table, $data, $format = null ) {
46
+ return $this->wpdb_mixin->default_insert( $table, $data, $format );
47
+ }
48
+
49
+ /**
50
+ * Placeholder for apropriate wp_db method replacement.
51
+ * By default calls wp_db implementation
52
+ */
53
+ function query( $query ) {
54
+ return $this->wpdb_mixin->default_query( $query );
55
+ }
56
+
57
+ /**
58
+ * Placeholder for apropriate wp_db method replacement.
59
+ * By default calls wp_db implementation
60
+ */
61
+ function replace( $table, $data, $format = null ) {
62
+ return $this->wpdb_mixin->default_replace( $table, $data, $format );
63
+ }
64
+
65
+ /**
66
+ * Placeholder for apropriate wp_db method replacement.
67
+ * By default calls wp_db implementation
68
+ */
69
+ function update( $table, $data, $where, $format = null, $where_format = null ) {
70
+ return $this->wpdb_mixin->default_update( $table, $data, $where, $format, $where_format );
71
+ }
72
+
73
+ /**
74
+ * Placeholder for apropriate wp_db method replacement.
75
+ * By default calls wp_db implementation
76
+ */
77
+ function delete( $table, $where, $where_format = null ) {
78
+ return $this->wpdb_mixin->default_delete( $table, $where, $where_format );
79
+ }
80
+
81
+ /**
82
+ * Placeholder for apropriate wp_db method replacement.
83
+ * By default calls wp_db implementation
84
+ */
85
+ function init_charset() {
86
+ return $this->wpdb_mixin->default_init_charset();
87
+ }
88
+
89
+ /**
90
+ * Placeholder for apropriate wp_db method replacement.
91
+ * By default calls wp_db implementation
92
+ */
93
+ function set_charset( $dbh, $charset = null, $collate = null ) {
94
+ return $this->wpdb_mixin->default_set_charset( $dbh, $charset, $collate );
95
+ }
96
+
97
+ /**
98
+ * Placeholder for apropriate wp_db method replacement.
99
+ * By default calls wp_db implementation
100
+ */
101
+ function flush() {
102
+ return $this->wpdb_mixin->default_flush();
103
+ }
104
+
105
+ /**
106
+ * Placeholder for apropriate wp_db method replacement.
107
+ * By default calls wp_db implementation
108
+ */
109
+ function check_database_version( $dbh_or_table = false ) {
110
+ return $this->wpdb_mixin->default_check_database_version( $dbh_or_table );
111
+ }
112
+
113
+ /**
114
+ * Placeholder for apropriate wp_db method replacement.
115
+ * By default calls wp_db implementation
116
+ */
117
+ function supports_collation( $dbh_or_table = false ) {
118
+ return $this->wpdb_mixin->default_supports_collation( $dbh_or_table );
119
+ }
120
+
121
+ /**
122
+ * Placeholder for apropriate wp_db method replacement.
123
+ * By default calls wp_db implementation
124
+ */
125
+ function has_cap( $db_cap, $dbh_or_table = false ) {
126
+ return $this->wpdb_mixin->default_has_cap( $db_cap, $dbh_or_table );
127
+ }
128
+
129
+ /**
130
+ * Placeholder for apropriate wp_db method replacement.
131
+ * By default calls wp_db implementation
132
+ */
133
+ function db_version( $dbh_or_table = false ) {
134
+ return $this->wpdb_mixin->default_db_version( $dbh_or_table );
135
+ }
136
+
137
+ public function w3tc_footer_comment( $strings ) {
138
+ return $strings;
139
+ }
140
+
141
+ public function w3tc_usage_statistics_of_request( $storage ) {
142
+ }
143
+
144
+ public function flush_cache() {
145
+ return true;
146
+ }
147
+ }
DbCache_WpdbInjection_QueryCaching.php ADDED
@@ -0,0 +1,666 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class DbCache_WpdbInjection_QueryCaching
6
+ */
7
+ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
8
+ /**
9
+ * Array of queries
10
+ *
11
+ * @var array
12
+ */
13
+ var $query_stats = array();
14
+
15
+ /**
16
+ * Queries total
17
+ *
18
+ * @var integer
19
+ */
20
+ var $query_total = 0;
21
+
22
+ /**
23
+ * Query cache hits
24
+ *
25
+ * @var integer
26
+ */
27
+ var $query_hits = 0;
28
+
29
+ /**
30
+ * Query cache misses
31
+ *
32
+ * @var integer
33
+ */
34
+ var $query_misses = 0;
35
+
36
+ /**
37
+ * Time total
38
+ *
39
+ * @var integer
40
+ */
41
+ var $time_total = 0;
42
+
43
+ /**
44
+ * Config
45
+ */
46
+ var $_config = null;
47
+
48
+ /**
49
+ * Lifetime
50
+ *
51
+ * @var integer
52
+ */
53
+ var $_lifetime = null;
54
+
55
+ /**
56
+ * Request-global cache reject reason
57
+ * null until filled
58
+ *
59
+ * @var string
60
+ */
61
+ private $cache_reject_reason = null;
62
+
63
+ /**
64
+ * Request-global check reject scope
65
+ * false until set
66
+ *
67
+ * @var bool
68
+ */
69
+ private $cache_reject_request_wide = false;
70
+ private $debug = false;
71
+ private $reject_logged = false;
72
+ private $reject_constants;
73
+
74
+ /**
75
+ * Result of check if caching is possible at the level of current http request
76
+ * null until filled
77
+ */
78
+ private $can_cache_once_per_request_result = null;
79
+
80
+ /*
81
+ * @param string $dbuser
82
+ * @param string $dbpassword
83
+ * @param string $dbname
84
+ * @param string $dbhost
85
+ */
86
+ function __construct() {
87
+ $c = Dispatcher::config();
88
+ $this->_config = $c;
89
+ $this->_lifetime = $c->get_integer( 'dbcache.lifetime' );
90
+ $this->debug = $c->get_boolean( 'dbcache.debug' );
91
+ $this->reject_logged = $c->get_boolean( 'dbcache.reject.logged' );
92
+ $this->reject_constants = $c->get_array( 'dbcache.reject.constants' );
93
+ }
94
+
95
+ /**
96
+ * Executes query
97
+ *
98
+ * @param string $query
99
+ * @return integer
100
+ */
101
+ function query( $query ) {
102
+ if ( !$this->wpdb_mixin->ready ) {
103
+ return $this->next_injection->query( $query );
104
+ }
105
+
106
+ $reason = '';
107
+ $cached = false;
108
+ $data = false;
109
+ $time_total = 0;
110
+
111
+ $this->query_total++;
112
+
113
+ $caching = $this->_can_cache( $query, $reason );
114
+ if ( preg_match( '~^\s*start transaction\b~is', $query ) ) {
115
+ $this->cache_reject_reason = 'transaction';
116
+ $caching = false;
117
+ }
118
+
119
+ if ( preg_match( '~^\s*insert\b|^\s*delete\b|^\s*update\b|^\s*replace\b|^\s*commit\b|^\s*truncate\b~is', $query ) ) {
120
+ if ( $caching ) {
121
+ $this->cache_reject_reason = 'modification query';
122
+ $caching = false;
123
+ }
124
+
125
+ $group = $this->_get_group( $query );
126
+ $this->_flush_cache_group( $group );
127
+ }
128
+
129
+ if ( $caching ) {
130
+ $this->wpdb_mixin->timer_start();
131
+ //$cache_key = $this->_get_cache_key($query);
132
+ $cache = $this->_get_cache();
133
+ $group = $this->_get_group( $query );
134
+ $data = $cache->get( md5( $query ), $group );
135
+ $time_total = $this->wpdb_mixin->timer_stop();
136
+ }
137
+
138
+ if ( is_array( $data ) ) {
139
+ $cached = true;
140
+ $this->query_hits++;
141
+
142
+ $this->wpdb_mixin->last_error = $data['last_error'];
143
+ $this->wpdb_mixin->last_query = $data['last_query'];
144
+ $this->wpdb_mixin->last_result = $data['last_result'];
145
+ $this->wpdb_mixin->col_info = $data['col_info'];
146
+ $this->wpdb_mixin->num_rows = $data['num_rows'];
147
+
148
+ $return_val = $data['return_val'];
149
+ } else {
150
+ $this->query_misses++;
151
+
152
+ $this->wpdb_mixin->timer_start();
153
+ $return_val = $this->next_injection->query( $query );
154
+ $time_total = $this->wpdb_mixin->timer_stop();
155
+
156
+ if ( $caching ) {
157
+ $data = array(
158
+ 'last_error' => $this->wpdb_mixin->last_error,
159
+ 'last_query' => $this->wpdb_mixin->last_query,
160
+ 'last_result' => $this->wpdb_mixin->last_result,
161
+ 'col_info' => $this->wpdb_mixin->col_info,
162
+ 'num_rows' => $this->wpdb_mixin->num_rows,
163
+ 'return_val' => $return_val
164
+ );
165
+
166
+ $cache = $this->_get_cache();
167
+ $group = $this->_get_group( $query );
168
+ $cache->set( md5( $query ), $data, $this->_lifetime, $group );
169
+ }
170
+ }
171
+
172
+ if ( $this->debug ) {
173
+ $this->query_stats[] = array(
174
+ 'query' => $query,
175
+ 'caching' => $caching,
176
+ 'reason' => $reason,
177
+ 'cached' => $cached,
178
+ 'data_size' => ( $data ? strlen( serialize( $data ) ) : 0 ),
179
+ 'time_total' => $time_total
180
+ );
181
+ }
182
+
183
+ $this->time_total += $time_total;
184
+
185
+ return $return_val;
186
+ }
187
+
188
+ /**
189
+ * Initializes object, calls underlying processor
190
+ */
191
+ function initialize() {
192
+ return $this->next_injection->initialize();
193
+ }
194
+
195
+ /**
196
+ * Insert a row into a table.
197
+ *
198
+ * @param string $table
199
+ * @param array $data
200
+ * @param array|string $format
201
+ * @return int|false
202
+ */
203
+ function insert( $table, $data, $format = null ) {
204
+ return $this->next_injection->insert( $table, $data, $format );
205
+ }
206
+
207
+ /**
208
+ * Replace a row into a table.
209
+ *
210
+ * @param string $table
211
+ * @param array $data
212
+ * @param array|string $format
213
+ * @return int|false
214
+ */
215
+ function replace( $table, $data, $format = null ) {
216
+ $group = $this->_get_group( $table );
217
+ $this->_flush_cache_group( $group );
218
+ return $this->next_injection->replace( $table, $data, $format );
219
+ }
220
+
221
+ /**
222
+ * Update a row in the table
223
+ *
224
+ * @param string $table
225
+ * @param array $data
226
+ * @param array $where
227
+ * @param array|string $format
228
+ * @param array|string $format_where
229
+ * @return int|false
230
+ */
231
+ function update( $table, $data, $where, $format = null, $where_format = null ) {
232
+ $group = $this->_get_group( $table );
233
+ $this->_flush_cache_group( $group );
234
+ return $this->next_injection->update( $table, $data, $where, $format, $where_format );
235
+ }
236
+
237
+ /**
238
+ * Deletes from table
239
+ */
240
+ function delete( $table, $where, $where_format = null ) {
241
+ $group = $this->_get_group( $table );
242
+ $this->_flush_cache_group( $group );
243
+ return $this->next_injection->delete( $table, $where, $where_format );
244
+ }
245
+
246
+ /**
247
+ * Flushes cache
248
+ *
249
+ * @return boolean
250
+ */
251
+ function flush_cache() {
252
+ return $this->_flush_cache_group( 'all' );
253
+ }
254
+
255
+ private function _flush_cache_group( $group ) {
256
+ $cache = $this->_get_cache();
257
+ $flush_groups = $this->_get_flush_groups( $group );
258
+ $v = true;
259
+
260
+ foreach ( $flush_groups as $f_group )
261
+ $v &= $cache->flush( $f_group );
262
+
263
+ return $v;
264
+ }
265
+
266
+ /**
267
+ * Returns cache object
268
+ *
269
+ * @return W3_Cache_Base
270
+ */
271
+ function _get_cache() {
272
+ static $cache = array();
273
+
274
+ if ( !isset( $cache[0] ) ) {
275
+ $engine = $this->_config->get_string( 'dbcache.engine' );
276
+
277
+ switch ( $engine ) {
278
+ case 'memcached':
279
+ $engineConfig = array(
280
+ 'servers' => $this->_config->get_array( 'dbcache.memcached.servers' ),
281
+ 'persistent' => $this->_config->get_boolean( 'dbcache.memcached.persistent' ),
282
+ 'aws_autodiscovery' => $this->_config->get_boolean( 'dbcache.memcached.aws_autodiscovery' ),
283
+ 'username' => $this->_config->get_boolean( 'dbcache.memcached.username' ),
284
+ 'password' => $this->_config->get_boolean( 'dbcache.memcached.password' )
285
+ );
286
+ break;
287
+
288
+ case 'redis':
289
+ $engineConfig = array(
290
+ 'servers' => $this->_config->get_array( 'dbcache.redis.servers' ),
291
+ 'persistent' => $this->_config->get_boolean( 'dbcache.redis.persistent' ),
292
+ 'dbid' => $this->_config->get_boolean( 'dbcache.redis.dbid' ),
293
+ 'password' => $this->_config->get_boolean( 'dbcache.redis.password' )
294
+ );
295
+ break;
296
+
297
+ case 'file':
298
+ $engineConfig = array(
299
+ 'use_wp_hash' => true,
300
+ 'section' => 'db',
301
+ 'locking' => $this->_config->get_boolean( 'dbcache.file.locking' ),
302
+ 'flush_timelimit' => $this->_config->get_integer( 'timelimit.cache_flush' )
303
+ );
304
+ break;
305
+
306
+ default:
307
+ $engineConfig = array();
308
+ }
309
+ $engineConfig['module'] = 'dbcache';
310
+ $engineConfig['host'] = Util_Environment::host();
311
+ $engineConfig['instance_id'] = Util_Environment::instance_id();
312
+
313
+ $cache[0] = Cache::instance( $engine, $engineConfig );
314
+ }
315
+
316
+ return $cache[0];
317
+ }
318
+
319
+ /**
320
+ * Check if can cache sql
321
+ *
322
+ * @param string $sql
323
+ * @param string $cache_reject_reason
324
+ * @return boolean
325
+ */
326
+ function _can_cache( $sql, &$cache_reject_reason ) {
327
+ /**
328
+ * Skip if request-wide reject reason specified.
329
+ * Note - as a result requedt-wide checks are done only once per request
330
+ */
331
+ if ( !is_null( $this->cache_reject_reason ) ) {
332
+ $cache_reject_reason = $this->cache_reject_reason;
333
+ $this->cache_reject_request_wide = true;
334
+ return false;
335
+ }
336
+
337
+ /**
338
+ * Do once-per-request check if needed
339
+ */
340
+ if ( is_null( $this->can_cache_once_per_request_result ) ) {
341
+ $this->can_cache_once_per_request_result = $this->_can_cache_once_per_request();
342
+ if ( !$this->can_cache_once_per_request_result ) {
343
+ $this->cache_reject_request_wide = true;
344
+ return false;
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Check for constants
350
+ */
351
+ foreach ( $this->reject_constants as $name ) {
352
+ if ( defined( $name ) && constant( $name ) ) {
353
+ $this->cache_reject_reason = $name . ' constant defined';
354
+ $cache_reject_reason = $this->cache_reject_reason;
355
+
356
+ return false;
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Check for AJAX requests
362
+ */
363
+ $ajax_skip = false;
364
+ if ( defined( 'DOING_AJAX' ) ) {
365
+ // wp_admin is always defined for ajax requests, check by referrer
366
+ if ( strpos( $_SERVER['HTTP_REFERER'], '/wp-admin/' ) === false )
367
+ $ajax_skip = true;
368
+ }
369
+
370
+ /**
371
+ * Skip if admin
372
+ */
373
+ if ( defined( 'WP_ADMIN' ) && !$ajax_skip ) {
374
+ $this->cache_reject_reason = 'WP_ADMIN';
375
+ $cache_reject_reason = $this->cache_reject_reason;
376
+
377
+ return false;
378
+ }
379
+
380
+ /**
381
+ * Skip if SQL is rejected
382
+ */
383
+ if ( !$this->_check_sql( $sql ) ) {
384
+ $cache_reject_reason = 'query not cacheable';
385
+
386
+ return false;
387
+ }
388
+
389
+ /**
390
+ * Skip if user is logged in
391
+ */
392
+ if ( $this->reject_logged && !$this->_check_logged_in() ) {
393
+ $this->cache_reject_reason = 'user.logged_in';
394
+ $cache_reject_reason = $this->cache_reject_reason;
395
+
396
+ return false;
397
+ }
398
+
399
+ return true;
400
+ }
401
+
402
+ /**
403
+ * Check if can cache sql, checks which have constant results during whole request
404
+ *
405
+ * @return boolean
406
+ */
407
+ function _can_cache_once_per_request() {
408
+ /**
409
+ * Skip if disabled
410
+ */
411
+ if ( !$this->_config->get_boolean( 'dbcache.enabled' ) ) {
412
+ $this->cache_reject_reason = 'dbcache.disabled';
413
+
414
+ return false;
415
+ }
416
+
417
+ /**
418
+ * Skip if request URI is rejected
419
+ */
420
+ if ( !$this->_check_request_uri() ) {
421
+ $this->cache_reject_reason = 'request';
422
+ return false;
423
+ }
424
+
425
+ /**
426
+ * Skip if cookie is rejected
427
+ */
428
+ if ( !$this->_check_cookies() ) {
429
+ $this->cache_reject_reason = 'cookie';
430
+ return false;
431
+ }
432
+
433
+ return true;
434
+ }
435
+
436
+ /**
437
+ * Check SQL
438
+ *
439
+ * @param string $sql
440
+ * @return boolean
441
+ */
442
+ function _check_sql( $sql ) {
443
+
444
+ $auto_reject_strings = $this->_config->get_array( 'dbcache.reject.words' );
445
+
446
+ if ( preg_match( '~' . implode( '|', $auto_reject_strings ) . '~is', $sql ) ) {
447
+ return false;
448
+ }
449
+
450
+ $reject_sql = $this->_config->get_array( 'dbcache.reject.sql' );
451
+
452
+ foreach ( $reject_sql as $expr ) {
453
+ $expr = trim( $expr );
454
+ $expr = str_replace( '{prefix}', $this->wpdb_mixin->prefix, $expr );
455
+ if ( $expr != '' && preg_match( '~' . $expr . '~i', $sql ) ) {
456
+ return false;
457
+ }
458
+ }
459
+
460
+ return true;
461
+ }
462
+
463
+ /**
464
+ * Check request URI
465
+ *
466
+ * @return boolean
467
+ */
468
+ function _check_request_uri() {
469
+ $auto_reject_uri = array(
470
+ 'wp-login',
471
+ 'wp-register'
472
+ );
473
+
474
+ foreach ( $auto_reject_uri as $uri ) {
475
+ if ( strstr( $_SERVER['REQUEST_URI'], $uri ) !== false ) {
476
+ return false;
477
+ }
478
+ }
479
+
480
+ $reject_uri = $this->_config->get_array( 'dbcache.reject.uri' );
481
+ $reject_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $reject_uri );
482
+
483
+ foreach ( $reject_uri as $expr ) {
484
+ $expr = trim( $expr );
485
+ if ( $expr != '' && preg_match( '~' . $expr . '~i', $_SERVER['REQUEST_URI'] ) ) {
486
+ return false;
487
+ }
488
+ }
489
+
490
+ return true;
491
+ }
492
+
493
+ /**
494
+ * Checks for WordPress cookies
495
+ *
496
+ * @return boolean
497
+ */
498
+ function _check_cookies() {
499
+ foreach ( array_keys( $_COOKIE ) as $cookie_name ) {
500
+ if ( $cookie_name == 'wordpress_test_cookie' ) {
501
+ continue;
502
+ }
503
+ if ( preg_match( '/^wp-postpass|^comment_author/', $cookie_name ) ) {
504
+ return false;
505
+ }
506
+ }
507
+
508
+ foreach ( $this->_config->get_array( 'dbcache.reject.cookie' ) as $reject_cookie ) {
509
+ foreach ( array_keys( $_COOKIE ) as $cookie_name ) {
510
+ if ( strstr( $cookie_name, $reject_cookie ) !== false ) {
511
+ return false;
512
+ }
513
+ }
514
+ }
515
+
516
+ return true;
517
+ }
518
+
519
+ /**
520
+ * Check if user is logged in
521
+ *
522
+ * @return boolean
523
+ */
524
+ function _check_logged_in() {
525
+ foreach ( array_keys( $_COOKIE ) as $cookie_name ) {
526
+ if ( strpos( $cookie_name, 'wordpress_logged_in' ) === 0 )
527
+ return false;
528
+ }
529
+
530
+ return true;
531
+ }
532
+
533
+ private function _get_group( $sql ) {
534
+ $sql = strtolower( $sql );
535
+ $matched = array();
536
+ $options = false. $comments = false;
537
+ $prefix = $this->wpdb_mixin->prefix;
538
+ $options = preg_match( '~' . $prefix . 'options~i', $sql );
539
+ $comments = preg_match( '~' . $prefix . '(comments|commentsmeta)~i', $sql );
540
+
541
+ if ( $options && $comments )
542
+ return 'options_comments';
543
+ if ( $options )
544
+ return 'options';
545
+ if ( $comments )
546
+ return 'comments';
547
+ return 'all';
548
+ }
549
+
550
+ private function _get_flush_groups( $group ) {
551
+ switch ( $group ) {
552
+ case 'all':
553
+ return array( 'all', 'options_comments', 'options', 'comments' );
554
+ case 'options_comments':
555
+ return array( 'options_comments', 'options', 'comments' );
556
+ case 'options':
557
+ case 'comments':
558
+ return array( 'options_comments', $group );
559
+ break;
560
+ default:
561
+ return array( $group );
562
+ }
563
+ }
564
+
565
+
566
+ public function get_reject_reason() {
567
+ if ( is_null( $this->cache_reject_reason ) )
568
+ return '';
569
+ $request_wide_string = $this->cache_reject_request_wide ?
570
+ ( function_exists( '__' ) ? __( 'Request-wide', 'w3-total-cache' ).' ' : 'Request ' ) : '';
571
+ return $request_wide_string . $this->_get_reject_reason_message( $this->cache_reject_reason );
572
+ }
573
+
574
+ /**
575
+ *
576
+ *
577
+ * @param unknown $key
578
+ * @return string|void
579
+ */
580
+ private function _get_reject_reason_message( $key ) {
581
+ if ( !function_exists( '__' ) )
582
+ return $key;
583
+ switch ( $key ) {
584
+ case 'dbcache.disabled':
585
+ return __( 'Database caching is disabled', 'w3-total-cache' );
586
+ case 'DONOTCACHEDB':
587
+ return __( 'DONOTCACHEDB constant is defined', 'w3-total-cache' );
588
+ case 'DOING_AJAX':
589
+ return __( 'Doing AJAX', 'w3-total-cache' );
590
+ case 'request':
591
+ return __( 'Request URI is rejected', 'w3-total-cache' );
592
+ case 'cookie':
593
+ return __( 'Cookie is rejected', 'w3-total-cache' );
594
+ case 'DOING_CRONG':
595
+ return __( 'Doing cron', 'w3-total-cache' );
596
+ case 'APP_REQUEST':
597
+ return __( 'Application request', 'w3-total-cache' );
598
+ case 'XMLRPC_REQUEST':
599
+ return __( 'XMLRPC request', 'w3-total-cache' );
600
+ case 'WP_ADMIN':
601
+ return __( 'wp-admin', 'w3-total-cache' );
602
+ case 'SHORTINIT':
603
+ return __( 'Short init', 'w3-total-cache' );
604
+ case 'query':
605
+ return __( 'Query is rejected', 'w3-total-cache' );
606
+ case 'user.logged_in':
607
+ return __( 'User is logged in', 'w3-total-cache' );
608
+ default:
609
+ return $key;
610
+ }
611
+ }
612
+
613
+ public function w3tc_footer_comment( $strings ) {
614
+ if ( $this->debug ) {
615
+ $strings[] = "Db cache debug info:";
616
+ $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ) );
617
+ $strings[] = sprintf( "%s%d", str_pad( 'Total queries: ', 20 ), $this->query_total );
618
+ $strings[] = sprintf( "%s%d", str_pad( 'Cached queries: ', 20 ), $this->query_hits );
619
+ $strings[] = sprintf( "%s%.4f", str_pad( 'Total query time: ', 20 ), $this->time_total );
620
+
621
+ if ( count( $this->query_stats ) ) {
622
+ $strings[] = "SQL info:";
623
+ $strings[] = sprintf( "%s | %s | %s | % s | %s | %s",
624
+ str_pad( '#', 5, ' ', STR_PAD_LEFT ), str_pad( 'Time (s)', 8, ' ', STR_PAD_LEFT ),
625
+ str_pad( 'Caching (Reject reason)', 30, ' ', STR_PAD_BOTH ),
626
+ str_pad( 'Status', 10, ' ', STR_PAD_BOTH ),
627
+ str_pad( 'Data size (b)', 13, ' ', STR_PAD_LEFT ),
628
+ 'Query' );
629
+
630
+ foreach ( $this->query_stats as $index => $query ) {
631
+ $strings[] = sprintf( "%s | %s | %s | %s | %s | %s",
632
+ str_pad( $index + 1, 5, ' ', STR_PAD_LEFT ),
633
+ str_pad( round( $query['time_total'], 4 ), 8, ' ', STR_PAD_LEFT ),
634
+ str_pad( ( $query['caching'] ? 'enabled'
635
+ : sprintf( 'disabled (%s)', $query['reason'] ) ), 30, ' ', STR_PAD_BOTH ),
636
+ str_pad( ( $query['cached'] ? 'cached' : 'not cached' ), 10, ' ', STR_PAD_BOTH ),
637
+ str_pad( $query['data_size'], 13, ' ', STR_PAD_LEFT ),
638
+ trim( $query['query'] ) );
639
+ }
640
+ }
641
+ } else {
642
+ $reason = $this->get_reject_reason();
643
+ $append = ( $reason ? sprintf( ' (%s)', $reason ) : '' );
644
+
645
+ if ( $this->query_hits ) {
646
+ $strings[] = sprintf(
647
+ __( 'Database Caching %d/%d queries in %.3f seconds using %s%s', 'w3-total-cache' ),
648
+ $this->query_hits, $this->query_total, $this->time_total,
649
+ Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ),
650
+ $append );
651
+ } else {
652
+ $strings[] = sprintf(
653
+ __( 'Database Caching using %s%s', 'w3-total-cache' ),
654
+ Cache::engine_name( $this->_config->get_string( 'dbcache.engine' ) ),
655
+ $append );
656
+ }
657
+ }
658
+
659
+ return $strings;
660
+ }
661
+
662
+ public function w3tc_usage_statistics_of_request( $storage ) {
663
+ $storage->counter_add( 'dbcache_calls_total', $this->query_total );
664
+ $storage->counter_add( 'dbcache_calls_hits', $this->query_hits );
665
+ }
666
+ }
Dispatcher.php ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Interplugin communication
6
+ */
7
+ class Dispatcher {
8
+ /**
9
+ * return component instance
10
+ */
11
+ static public function component( $class ) {
12
+ static $instances = array();
13
+
14
+ if ( !isset( $instances[$class] ) ) {
15
+ $full_class = '\\W3TC\\' . $class;
16
+ $instances[$class] = new $full_class();
17
+ }
18
+
19
+ $v = $instances[$class]; // Don't return reference
20
+ return $v;
21
+ }
22
+
23
+ static public function config() {
24
+ return self::component( 'Config' );
25
+ }
26
+
27
+ static public function config_master() {
28
+ static $config_master = null;
29
+
30
+ if ( is_null( $config_master ) ) {
31
+ $config_master = new Config( 0 );
32
+ }
33
+
34
+ return $config_master;
35
+ }
36
+
37
+ static public function config_state() {
38
+ if ( Util_Environment::blog_id() <= 0 )
39
+ return self::config_state_master();
40
+
41
+ static $config_state = null;
42
+
43
+ if ( is_null( $config_state ) )
44
+ $config_state = new ConfigState( false );
45
+
46
+ return $config_state;
47
+ }
48
+
49
+ static public function config_state_master() {
50
+ static $config_state = null;
51
+
52
+ if ( is_null( $config_state ) )
53
+ $config_state = new ConfigState( true );
54
+
55
+ return $config_state;
56
+ }
57
+
58
+ static public function config_state_note() {
59
+ static $o = null;
60
+
61
+ if ( is_null( $o ) )
62
+ $o = new ConfigStateNote( self::config_state_master(),
63
+ self::config_state() );
64
+
65
+ return $o;
66
+ }
67
+
68
+ /**
69
+ * Checks if specific local url is uploaded to CDN
70
+ *
71
+ * @param string $url
72
+ * @return bool
73
+ */
74
+ static public function is_url_cdn_uploaded( $url ) {
75
+ $minify_enabled = self::config()->get_boolean( 'minify.enabled' );
76
+ if ( $minify_enabled ) {
77
+ $minify = Dispatcher::component( 'Minify_MinifiedFileRequestHandler' );
78
+ $data = $minify->get_url_custom_data( $url );
79
+ if ( is_array( $data ) && isset( $data['cdn.status'] ) && $data['cdn.status'] == 'uploaded' ) {
80
+ return true;
81
+ }
82
+ }
83
+ // supported only for minify-based urls, futher is not needed now
84
+ return false;
85
+ }
86
+
87
+ /**
88
+ * Creates file for CDN upload.
89
+ * Needed because minify can handle urls of non-existing files but CDN needs
90
+ * real file to upload it
91
+ */
92
+ static public function create_file_for_cdn( $filename ) {
93
+ $minify_enabled = self::config()->get_boolean( 'minify.enabled' );
94
+ if ( $minify_enabled ) {
95
+ $minify_document_root = Util_Environment::cache_blog_dir( 'minify' ) . '/';
96
+
97
+ if ( !substr( $filename, 0, strlen( $minify_document_root ) ) == $minify_document_root ) {
98
+ // unexpected file name
99
+ return;
100
+ }
101
+
102
+ $short_filename = substr( $filename, strlen( $minify_document_root ) );
103
+ $minify = Dispatcher::component( 'Minify_MinifiedFileRequestHandler' );
104
+
105
+ $data = $minify->process( $short_filename, true );
106
+
107
+ if ( !file_exists( $filename ) &&
108
+ isset( $data['content']['content'] ) ) {
109
+
110
+ if ( !file_exists( dirname( $filename ) ) )
111
+ Util_File::mkdir_from( dirname( $filename ), W3TC_CACHE_DIR );
112
+ }
113
+ @file_put_contents( $filename, $data['content']['content'] );
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Called on successful file upload to CDN
119
+ *
120
+ * @param unknown $file_name
121
+ */
122
+ static public function on_cdn_file_upload( $file_name ) {
123
+ $minify_enabled = self::config()->get_boolean( 'minify.enabled' );
124
+ if ( $minify_enabled ) {
125
+ $minify_document_root = Util_Environment::cache_blog_dir( 'minify' ) . '/';
126
+
127
+ if ( !substr( $file_name, 0, strlen( $minify_document_root ) ) == $minify_document_root ) {
128
+ // unexpected file name
129
+ return;
130
+ }
131
+
132
+ $short_file_name = substr( $file_name, strlen( $minify_document_root ) );
133
+ $minify = Dispatcher::component( 'Minify_MinifiedFileRequestHandler' );
134
+ $minify->set_file_custom_data( $short_file_name,
135
+ array( 'cdn.status' => 'uploaded' ) );
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Generates canonical header code for nginx if browsercache plugin has
141
+ * to generate it
142
+ *
143
+ * @param Config $config
144
+ * @param boolean $cdnftp if CDN FTP is used
145
+ * @param string $section
146
+ * @return string
147
+ */
148
+ static public function on_browsercache_rules_generation_for_section( $config, $cdnftp,
149
+ $section, $add_header_rules ) {
150
+ if ( $section != 'other' )
151
+ return '';
152
+ if ( self::canonical_generated_by( $config, $cdnftp ) != 'browsercache' )
153
+ return '';
154
+
155
+ return Util_RuleSnippet::canonical_without_location( $cdnftp,
156
+ $add_header_rules );
157
+ }
158
+
159
+ /**
160
+ * Called when minify going to process request of some minified file
161
+ */
162
+ static public function requested_minify_filename( $config, $file ) {
163
+ // browsercache may alter filestructure, allow it to remove its
164
+ // uniqualizator
165
+ if ( $config->get_boolean( 'browsercache.enabled' ) &&
166
+ $config->get_boolean( 'browsercache.rewrite' ) ) {
167
+ if ( preg_match( '~(.+)\.([0-9a-z]+)(\.[^.]+)$~', $file, $m ) )
168
+ $file = $m[1] . $m[3];
169
+ }
170
+ return $file;
171
+ }
172
+
173
+ /**
174
+ * Checks whether canonical should be generated or not by browsercache plugin
175
+ *
176
+ * @param Config $config
177
+ * @param boolean $cdnftp
178
+ * @return string|null
179
+ */
180
+ static public function canonical_generated_by( $config, $cdnftp = false ) {
181
+ if ( !self::_should_canonical_be_generated( $config, $cdnftp ) )
182
+ return null;
183
+
184
+ if ( Util_Environment::is_nginx() ) {
185
+ // in nginx - browsercache generates canonical if its enabled,
186
+ // since it does not allow multiple location tags
187
+ if ( $config->get_boolean( 'browsercache.enabled' ) )
188
+ return 'browsercache';
189
+ }
190
+
191
+ if ( $config->get_boolean( 'cdn.enabled' ) )
192
+ return 'cdn';
193
+
194
+ return null;
195
+ }
196
+
197
+ /**
198
+ * Basic check if canonical generation should be done
199
+ *
200
+ * @param Config $config
201
+ * @param boolean $cdnftp
202
+ * @return bool
203
+ */
204
+ static private function _should_canonical_be_generated( $config, $cdnftp ) {
205
+ if ( !$config->get_boolean( 'cdn.canonical_header' ) )
206
+ return false;
207
+
208
+ $cdncommon = Dispatcher::component( 'Cdn_Core' );
209
+
210
+ $cdn = $cdncommon->get_cdn();
211
+ return ( ( $config->get_string( 'cdn.engine' ) != 'ftp' || $cdnftp ) &&
212
+ $cdn->headers_support() == W3TC_CDN_HEADER_MIRRORING );
213
+ }
214
+
215
+ /**
216
+ * Checks whether canonical should be generated or not by browsercache plugin
217
+ *
218
+ * @param Config $config
219
+ * @return string|null
220
+ */
221
+ static public function allow_origin_generated_by( $config ) {
222
+ if ( $config->get_boolean( 'cdn.enabled' ) )
223
+ return 'cdn';
224
+
225
+ return null;
226
+ }
227
+
228
+ /**
229
+ * If BrowserCache should generate rules specific for CDN. Used with CDN FTP
230
+ *
231
+ * @param Config $config
232
+ * @return boolean;
233
+ */
234
+ static public function should_browsercache_generate_rules_for_cdn( $config ) {
235
+ if ( $config->get_boolean( 'cdn.enabled' ) &&
236
+ $config->get_string( 'cdn.engine' ) == 'ftp' ) {
237
+ $cdncommon = Dispatcher::component( 'Cdn_Core' );
238
+ $cdn = $cdncommon->get_cdn();
239
+ $domain = $cdn->get_domain();
240
+
241
+ if ( $domain )
242
+ return true;
243
+ }
244
+ return false;
245
+ }
246
+
247
+ /**
248
+ * Returns the domain used with the cdn.
249
+ *
250
+ * @param string
251
+ * @return string
252
+ */
253
+ static public function get_cdn_domain( $path = '' ) {
254
+ $cdncommon = Dispatcher::component( 'Cdn_Core' );
255
+ $cdn = $cdncommon->get_cdn();
256
+ return $cdn->get_domain( $path );
257
+ }
258
+
259
+ /**
260
+ * Usage statistics uses one of other module's cache
261
+ * to store its temporary data
262
+ */
263
+ static public function get_usage_statistics_cache() {
264
+ static $cache = null;
265
+ if ( is_null( $cache ) ) {
266
+ $c = Dispatcher::config();
267
+ if ( $c->get_boolean( 'objectcache.enabled' ) )
268
+ $provider = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
269
+ else if ( $c->get_boolean( 'dbcache.enabled' ) )
270
+ $provider = Dispatcher::component( 'DbCache_Core' );
271
+ else if ( $c->get_boolean( 'pgcache.enabled' ) )
272
+ $provider = Dispatcher::component( 'PgCache_ContentGrabber' );
273
+ else if ( $c->get_boolean( 'minify.enabled' ) )
274
+ $provider = Dispatcher::component( 'Minify_Core' );
275
+ else
276
+ return null;
277
+
278
+ $engineConfig = $provider->get_usage_statistics_cache_config();
279
+ $engineConfig['module'] = 'stats';
280
+ $engineConfig['blog_id'] = 0; // count wpmu-wide stats
281
+
282
+ if ( $engineConfig['engine'] == 'file' ) {
283
+ $engineConfig['cache_dir'] = Util_Environment::cache_dir( 'stats' );
284
+ }
285
+
286
+ $cache = Cache::instance( $engineConfig['engine'],
287
+ $engineConfig );
288
+ }
289
+
290
+ return $cache;
291
+ }
292
+
293
+ /**
294
+ * In a case request processing has been finished before WP initialized,
295
+ * but usage statistics metrics should be counted.
296
+ * To work properly $metrics_function has to be added also by plugin
297
+ * when add_action is available.
298
+ */
299
+ static public function usage_statistics_apply_before_init_and_exit(
300
+ $metrics_function ) {
301
+ $c = Dispatcher::config();
302
+ if ( !$c->get_boolean( 'stats.enabled' ) )
303
+ exit();
304
+
305
+ $core = Dispatcher::component( 'UsageStatistics_Core' );
306
+ $core->apply_metrics_before_init_and_exit( $metrics_function );
307
+ }
308
+ }
Enterprise_CacheFlush_MakeSnsEvent.php ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Purge using AmazonSNS object
6
+ */
7
+ class Enterprise_CacheFlush_MakeSnsEvent extends Enterprise_SnsBase {
8
+
9
+ private $messages = array();
10
+ private $messages_by_signature = array();
11
+
12
+ /**
13
+ * Flushes DB caches
14
+ *
15
+ */
16
+ function dbcache_flush() {
17
+ $this->_prepare_message( array( 'action' => 'dbcache_flush' ) );
18
+ }
19
+
20
+ /**
21
+ * Flushes minify caches
22
+ *
23
+ */
24
+ function minifycache_flush() {
25
+ $this->_prepare_message( array( 'action' => 'minifycache_flush' ) );
26
+ }
27
+
28
+ /**
29
+ * Flushes object caches
30
+ *
31
+ */
32
+ function objectcache_flush() {
33
+ $this->_prepare_message( array( 'action' => 'objectcache_flush' ) );
34
+ }
35
+
36
+ /**
37
+ * Flushes fragment caches
38
+ *
39
+ */
40
+ function fragmentcache_flush() {
41
+ $this->_prepare_message( array( 'action' => 'fragmentcache_flush' ) );
42
+ }
43
+
44
+ /**
45
+ * Flushes fragment cache based on group
46
+ *
47
+ */
48
+ function fragmentcache_flush_group( $group ) {
49
+ $this->_prepare_message( array( 'action' => 'fragmentcache_flush_group',
50
+ 'group' => $group ) );
51
+ }
52
+
53
+ /**
54
+ * Flushes query string
55
+ *
56
+ */
57
+ function browsercache_flush() {
58
+ $this->_prepare_message( array( 'action' => 'browsercache_flush' ) );
59
+ }
60
+
61
+ /**
62
+ * Purges Files from Varnish (If enabled) and CDN
63
+ *
64
+ */
65
+ function cdn_purge_files( $purgefiles ) {
66
+ $this->_prepare_message( array( 'action' => 'cdn_purge_files', 'purgefiles' => $purgefiles ) );
67
+ }
68
+
69
+ /**
70
+ * Performs garbage collection on the pgcache
71
+ */
72
+ function pgcache_cleanup() {
73
+ $this->_prepare_message( array( 'action' => 'pgcache_cleanup' ) );
74
+ }
75
+
76
+ /**
77
+ * Flushes the system APC
78
+ *
79
+ * @return bool
80
+ */
81
+ function opcache_flush() {
82
+ $this->_prepare_message( array( 'action' => 'opcache_flush' ) );
83
+ }
84
+
85
+ /**
86
+ * Reloads/compiles a PHP file.
87
+ *
88
+ * @param string $filename
89
+ * @return mixed
90
+ */
91
+ function opcache_flush_file( $filename ) {
92
+ return $this->_prepare_message( array(
93
+ 'action' => 'opcache_flush_file',
94
+ 'filename' => $filename ) );
95
+ }
96
+
97
+ /**
98
+ * Purges/Flushes post page
99
+ *
100
+ * @param unknown $post_id
101
+ * @return boolean
102
+ */
103
+ function flush_post( $post_id ) {
104
+ return $this->_prepare_message( array( 'action' => 'flush_post', 'post_id' => $post_id ) );
105
+ }
106
+
107
+ /**
108
+ * Purges/Flushes posts from caches
109
+ *
110
+ * @param unknown $extras
111
+ * @return boolean
112
+ */
113
+ function flush_posts( $extras ) {
114
+ return $this->_prepare_message( array(
115
+ 'action' => 'flush_posts',
116
+ 'extras' => $extras ) );
117
+ }
118
+
119
+ /**
120
+ * Purges/Flushes all enabled caches
121
+ *
122
+ * @return boolean
123
+ */
124
+ function flush_all( $extras ) {
125
+ return $this->_prepare_message( array(
126
+ 'action' => 'flush_all',
127
+ 'extras' => $extras
128
+ ) );
129
+ }
130
+
131
+ /**
132
+ * Purges/Flushes url
133
+ *
134
+ * @param string $url
135
+ * @return boolean
136
+ */
137
+ function flush_url( $url ) {
138
+ return $this->_prepare_message( array( 'action' => 'flush_url', 'url' => $url ) );
139
+ }
140
+
141
+ /**
142
+ * Makes get request to url specific to post, ie permalinks
143
+ *
144
+ * @param unknown $post_id
145
+ * @return mixed
146
+ */
147
+ function prime_post( $post_id ) {
148
+ return $this->_prepare_message( array( 'action' => 'prime_post', 'post_id' => $post_id ) );
149
+ }
150
+
151
+ /**
152
+ * Setups message list and if it should be combined or separate
153
+ *
154
+ * @param unknown $message
155
+ * @return boolean
156
+ */
157
+ private function _prepare_message( $message ) {
158
+ $message_signature = json_encode( $message );
159
+ if ( isset( $this->messages_by_signature[$message_signature] ) )
160
+ return true;
161
+ $this->messages_by_signature[$message_signature] = '*';
162
+ $this->messages[] = $message;
163
+
164
+ $action = $this->_get_action();
165
+ if ( !$action ) {
166
+ $this->execute_delayed_operations();
167
+ return true;
168
+ }
169
+
170
+ return true;
171
+ }
172
+
173
+ /**
174
+ * Sends messages stored in $messages
175
+ *
176
+ * @return boolean
177
+ */
178
+ public function execute_delayed_operations() {
179
+ if ( count( $this->messages ) <= 0 )
180
+ return true;
181
+
182
+ $this->_log( $this->_get_action() . ' sending messages' );
183
+
184
+ $message = array();
185
+ $message['actions'] = $this->messages;
186
+ $message['blog_id'] = Util_Environment::blog_id();
187
+ $message['host'] = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : null;
188
+ $message['hostname'] = @gethostname();
189
+ $v = json_encode( $message );
190
+
191
+ try {
192
+ $api = $this->_get_api();
193
+ if ( defined( 'WP_CLI' ) && WP_CLI )
194
+ $origin = 'WP CLI';
195
+ else
196
+ $origin = 'WP';
197
+ $this->_log( $origin . ' sending message ' . $v );
198
+ $this->_log( 'Host: ' . $message['host'] );
199
+ if ( isset( $_SERVER['REQUEST_URI'] ) )
200
+ $this->_log( 'URL: ' . $_SERVER['REQUEST_URI'] );
201
+ if ( function_exists( 'current_filter' ) )
202
+ $this->_log( 'Current WP hook: ' . current_filter() );
203
+
204
+ $backtrace = debug_backtrace();
205
+ $backtrace_optimized = array();
206
+ foreach ( $backtrace as $b ) {
207
+ $opt = isset( $b['function'] ) ? $b['function'] . ' ' : '';
208
+ $opt .= isset( $b['file'] ) ? $b['file'] . ' ' : '';
209
+ $opt .= isset( $b['line'] ) ? '#' . $b['line'] . ' ' : '';
210
+ $backtrace_optimized[] = $opt;
211
+
212
+ }
213
+ $this->_log( 'Backtrace ', $backtrace_optimized );
214
+
215
+ $r = $api->publish( $this->_topic_arn, $v );
216
+ if ( $r->status != 200 ) {
217
+ $this->_log( "Error: {$r->body->Error->Message}" );
218
+ return false;
219
+ }
220
+ } catch ( \Exception $e ) {
221
+ $this->_log( 'Error ' . $e->getMessage() );
222
+ return false;
223
+ }
224
+
225
+ // on success - reset messages array, but not hash (not resent repeatedly the same messages)
226
+ $this->messages = array();
227
+
228
+ return true;
229
+ }
230
+
231
+ /**
232
+ * Gets the current running WP action if any. Returns empty string if not found.
233
+ *
234
+ * @return string
235
+ */
236
+ private function _get_action() {
237
+ $action = '';
238
+ if ( function_exists( 'current_filter' ) )
239
+ $action = current_filter();
240
+ return $action;
241
+ }
242
+ }
Enterprise_Dbcache_WpdbInjection_Cluster.php ADDED
@@ -0,0 +1,813 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * class Enterprise_Dbcache_WpdbInjection_Cluster
6
+ * Support of database cluster
7
+ */
8
+ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection {
9
+ /**
10
+ * Whether to check with fsockopen prior to mysql_connect.
11
+ *
12
+ * @var bool
13
+ */
14
+ var $check_tcp_responsiveness = true;
15
+
16
+ /**
17
+ * Minimum number of connections to try before bailing
18
+ *
19
+ * @var int
20
+ */
21
+ var $min_tries = 3;
22
+
23
+ /**
24
+ * Whether to use mysql_pconnect instead of mysql_connect
25
+ *
26
+ * @var bool
27
+ */
28
+ var $persistent = false;
29
+
30
+ /**
31
+ * Cache of tables-to-dataset mapping if blog_dataset callback defined
32
+ *
33
+ * @var array
34
+ */
35
+ var $_blog_to_dataset = array();
36
+
37
+ /**
38
+ * Optional directory of callbacks to determine datasets from queries
39
+ *
40
+ * @var array
41
+ */
42
+ var $_callbacks = array();
43
+
44
+ /**
45
+ * The multi-dimensional array of datasets and servers
46
+ *
47
+ * @var array
48
+ */
49
+ var $_cluster_servers = array();
50
+
51
+ /**
52
+ * Zone where application runs
53
+ *
54
+ * @var array
55
+ */
56
+ var $_current_zone = array( 'name' => 'all', 'zone_priorities' => array( 'all' ) );
57
+
58
+ /**
59
+ * Established connections
60
+ *
61
+ * @var array
62
+ */
63
+ var $_connections;
64
+
65
+ /**
66
+ * After any SQL_CALC_FOUND_ROWS query, the query "SELECT FOUND_ROWS()"
67
+ * is sent and the mysql result resource stored here. The next query
68
+ * for FOUND_ROWS() will retrieve this. We do this to prevent any
69
+ * intervening queries from making FOUND_ROWS() inaccessible. You may
70
+ * prevent this by adding "NO_SELECT_FOUND_ROWS" in a comment.
71
+ *
72
+ * @var resource
73
+ */
74
+ var $_last_found_rows_result;
75
+
76
+ /**
77
+ * The last table that was queried
78
+ *
79
+ * @var string
80
+ */
81
+ var $_last_table;
82
+
83
+ /**
84
+ * Reject reason
85
+ */
86
+ var $_reject_reason = null;
87
+
88
+ /**
89
+ * Send Reads To Masters. This disables slave connections while true.
90
+ * Otherwise it is an array of written tables.
91
+ *
92
+ * @var array
93
+ */
94
+ var $_send_reads_to_master = false;
95
+
96
+
97
+ /**
98
+ * Send all request to master db if user is in administration.
99
+ *
100
+ * @var bool
101
+ */
102
+ var $use_master_in_backend = true;
103
+
104
+ /**
105
+ * Which charset to use for connections
106
+ *
107
+ * @var string
108
+ */
109
+ var $charset = null;
110
+
111
+ /**
112
+ * Which collate to use for connections
113
+ *
114
+ * @var string
115
+ */
116
+ var $collate = null;
117
+
118
+ /**
119
+ * Initializes object
120
+ */
121
+ function initialize() {
122
+ global $wpdb_cluster;
123
+ $wpdb_cluster = $this;
124
+
125
+ if ( file_exists( WP_CONTENT_DIR . '/db-cluster-config.php' ) ) {
126
+ // The config file resides in WP_CONTENT_DIR
127
+ require WP_CONTENT_DIR . '/db-cluster-config.php';
128
+ } else {
129
+ $this->_reject_reason = 'db-cluster-config.php configuration file not found, ' .
130
+ 'using single-server configuration';
131
+ $this->next_injection->initialize();
132
+ return;
133
+ }
134
+
135
+ if ( WP_DEBUG )
136
+ $this->wpdb_mixin->show_errors();
137
+
138
+ $this->wpdb_mixin->dbh = null;
139
+ $this->wpdb_mixin->ready = true;
140
+
141
+ $this->init_charset();
142
+ }
143
+
144
+ /**
145
+ * Sets default charset and collate
146
+ * If DB_CHARSET not set uses utf8
147
+ * If DB_COLLATE not set uses utf8_general_ci if multisite.
148
+ */
149
+ function init_charset() {
150
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
151
+ if ( defined( 'DB_COLLATE' ) && DB_COLLATE )
152
+ $this->wpdb_mixin->collate = DB_COLLATE;
153
+ else
154
+ $this->wpdb_mixin->collate = 'utf8_general_ci';
155
+ } elseif ( defined( 'DB_COLLATE' ) ) {
156
+ $this->wpdb_mixin->collate = DB_COLLATE;
157
+ }
158
+
159
+ if ( defined( 'DB_CHARSET' ) )
160
+ $this->wpdb_mixin->charset = DB_CHARSET;
161
+ else
162
+ $this->wpdb_mixin->charset = 'utf8';
163
+ }
164
+
165
+ /**
166
+ * Adds zone which defines priorities based on current application location
167
+ *
168
+ * @param array $zone
169
+ * keys:
170
+ * name
171
+ * SERVER_NAME
172
+ * zone_priorities array
173
+ */
174
+ function add_zone( $zone ) {
175
+ if ( $this->_is_current_zone( $zone ) ) {
176
+ if ( !isset( $zone['zone_priorities'] ) || !is_array( $zone['zone_priorities'] ) ) {
177
+ die( 'zone_priorities key must be defined' );
178
+ }
179
+
180
+ $this->_current_zone = $zone;
181
+ $this->_run_callbacks( 'current_zone_set', $zone );
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Add the connection parameters for a database
187
+ *
188
+ * @param array $db
189
+ * keys:
190
+ * name
191
+ * dataset
192
+ * read
193
+ * write
194
+ * host
195
+ * user
196
+ * password
197
+ * connect_function
198
+ * timeout
199
+ * zone
200
+ */
201
+ function add_database( $db ) {
202
+ $dataset = ( isset( $db['dataset'] ) ? $db['dataset'] : 'global' );
203
+ unset( $db['dataset'] );
204
+
205
+ $read = ( isset( $db['read'] ) ? $db['read'] : true );
206
+ unset( $db['read'] );
207
+ $write = ( isset( $db['write'] ) ? $db['write'] : true );
208
+ unset( $db['write'] );
209
+
210
+ $zone = ( isset( $db['zone'] ) ? $db['zone'] : 'all' );
211
+ unset( $db['zone'] );
212
+
213
+ if ( !isset( $db['timeout'] ) )
214
+ $db['timeout'] = 0.2;
215
+ if ( !isset( $db['name'] ) )
216
+ $db['name'] = $db['host'];
217
+
218
+ if ( $read )
219
+ $this->_cluster_servers[$dataset]['read'][$zone][] = $db;
220
+ if ( $write )
221
+ $this->_cluster_servers[$dataset]['write'][$zone][] = $db;
222
+ }
223
+
224
+ /**
225
+ * Add a callback to a group of callbacks.
226
+ * The default group is 'dataset', used to examine
227
+ * queries and determine dataset.
228
+ *
229
+ * @param object $callback
230
+ * @param string $group
231
+ */
232
+ function add_callback( $callback, $group = 'dataset' ) {
233
+ $this->_callbacks[$group][] = $callback;
234
+ }
235
+
236
+ /**
237
+ * Set a flag to prevent reading from slaves which might be lagging after a write
238
+ */
239
+ function send_reads_to_masters() {
240
+ $this->_send_reads_to_master = true;
241
+ }
242
+
243
+ /**
244
+ * If requests should be send to master or not.
245
+ *
246
+ * @return bool
247
+ */
248
+ function send_to_masters() {
249
+ return is_admin() && $this->use_master_in_backend;
250
+ }
251
+
252
+ /**
253
+ * Figure out which database server should handle the query, and connect to it.
254
+ *
255
+ * @param string $query
256
+ * @return resource mysql database connection
257
+ */
258
+ function db_connect( $query = '', $use_master = null ) {
259
+ $connect_function = $this->persistent ? 'mysql_pconnect' : 'mysql_connect';
260
+ if ( empty( $this->_cluster_servers ) )
261
+ return $this->_db_connect_fallback();
262
+
263
+ $this->wpdb_mixin->dbh = null;
264
+
265
+ if ( empty( $query ) )
266
+ return false;
267
+
268
+ $this->_last_table = $this->wpdb_mixin->table = $this->_get_table_from_query( $query );
269
+
270
+ $this->callback_result = $this->_run_callbacks( 'dataset', $query );
271
+ if ( !is_null( $this->callback_result ) )
272
+ $dataset = $this->callback_result;
273
+ elseif ( isset( $this->_callbacks['blog_dataset'] ) ) {
274
+ if ( preg_match( '/^' . $this->wpdb_mixin->base_prefix . '(\d+)_/i',
275
+ $this->wpdb_mixin->table, $matches ) ) {
276
+ $blog_id = $matches[1];
277
+
278
+ if ( isset( $this->_blog_to_dataset[$blog_id] ) )
279
+ $dataset = $this->_blog_to_dataset[$blog_id];
280
+ else {
281
+ $this->callback_result = $this->_run_callbacks( 'blog_dataset', $blog_id );
282
+ if ( !is_null( $this->callback_result ) ) {
283
+ $dataset = $this->callback_result;
284
+ $this->_blog_to_dataset[$blog_id] = $dataset;
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ $dataset = ( isset( $dataset ) ? $dataset : 'global' );
291
+ $this->dataset = $dataset;
292
+
293
+ // Determine whether the query must be sent to the master (a writable server)
294
+ if ( is_null( $use_master ) ) {
295
+ if ( $this->_send_reads_to_master || $this->send_to_masters() ) {
296
+ $use_master = true;
297
+ } elseif ( defined( 'DONOTBCLUSTER' ) && DONOTBCLUSTER ) {
298
+ $use_master = true;
299
+ } elseif ( $is_write = $this->_is_write_query( $query ) ) {
300
+ $use_master = true;
301
+ } else {
302
+ $use_master = false;
303
+ }
304
+ }
305
+
306
+ if ( $use_master ) {
307
+ $this->dbhname = $dataset . '__w';
308
+ $operation = 'write';
309
+ } else {
310
+ $this->dbhname = $dataset . '__r';
311
+ $operation = 'read';
312
+ }
313
+
314
+ // Try to reuse an existing connection
315
+ $dbh = $this->_db_connect_reuse_connection();
316
+ if ( is_resource( $dbh ) ) {
317
+ $this->wpdb_mixin->dbh = $dbh;
318
+ return $dbh;
319
+ }
320
+
321
+ if ( empty( $this->_cluster_servers[$dataset][$operation] ) )
322
+ return $this->wpdb_mixin->bail( "No databases available for dataset $dataset operation $operation" );
323
+
324
+ // Make a list of at least $this->min_tries connections to try, repeating as necessary.
325
+ $servers = array();
326
+ do {
327
+ foreach ( $this->_current_zone['zone_priorities'] as $zone ) {
328
+ if ( isset( $this->_cluster_servers[$dataset][$operation][$zone] ) ) {
329
+ $zone_servers = $this->_cluster_servers[$dataset][$operation][$zone];
330
+
331
+ if ( is_array( $zone_servers ) ) {
332
+ $indexes = array_keys( $zone_servers );
333
+ shuffle( $indexes );
334
+ foreach ( $indexes as $index )
335
+ $servers[] = compact( 'zone', 'index' );
336
+ }
337
+ }
338
+ }
339
+ } while ( count( $servers ) < $this->min_tries );
340
+ // Connect to a database server
341
+ $success = false;
342
+ $dbhname = $this->dbhname;
343
+
344
+ foreach ( $servers as $zone_index ) {
345
+ // $zone, $index
346
+ extract( $zone_index, EXTR_OVERWRITE );
347
+
348
+ // $host, $user, $password, $name, $read, $write [, $connect_function], $timeout
349
+ extract( $this->_cluster_servers[$dataset][$operation][$zone][$index], EXTR_OVERWRITE );
350
+
351
+ // Split host:port into $host and $port
352
+ if ( strpos( $host, ':' ) )
353
+ list( $host, $port ) = explode( ':', $host );
354
+
355
+ // Make sure there's always a port number
356
+ if ( empty( $port ) )
357
+ $port = 3306;
358
+
359
+ $this->wpdb_mixin->timer_start();
360
+
361
+ // Connect if necessary or possible
362
+ $tcp_responded = null;
363
+ if ( $this->check_tcp_responsiveness ) {
364
+ $tcp_responded = $this->_check_tcp_responsiveness( $host, $port, $timeout );
365
+ }
366
+
367
+ $dbh = null;
368
+ if ( is_null( $tcp_responded ) || $tcp_responded )
369
+ $dbh = @ $connect_function( "$host:$port", $user, $password, true );
370
+
371
+ $elapsed = $this->wpdb_mixin->timer_stop();
372
+
373
+ if ( is_resource( $dbh ) ) {
374
+ if ( mysql_select_db( $name, $dbh ) ) {
375
+ $this->_connections[$dbhname] = array(
376
+ 'dbh' => $dbh,
377
+ 'database_name' => $name );
378
+ $success = true;
379
+ break;
380
+ }
381
+ }
382
+ }
383
+
384
+ if ( !$success ) {
385
+ if ( !$use_master ) {
386
+ return $this->db_connect( $query, true );
387
+ }
388
+
389
+ return $this->wpdb_mixin->bail( "Unable to connect to $host:$port to $operation table '{$this->wpdb_mixin->table}' ($dataset)" );
390
+ }
391
+
392
+ $dbh = $this->_connections[$dbhname]['dbh'];
393
+ $this->wpdb_mixin->dbh = $dbh; // needed by $wpdb->_real_escape()
394
+ $this->set_charset( $dbh, $this->charset, $this->collate );
395
+
396
+ return $dbh;
397
+ }
398
+
399
+ /*
400
+ * Checks if this is our zone
401
+ *
402
+ * @param $zone array
403
+ * @return boolean
404
+ */
405
+ function _is_current_zone( $zone ) {
406
+ // obsolete
407
+ if ( isset( $zone['SERVER_NAME'] ) ) {
408
+ if ( $_SERVER['SERVER_NAME'] == $zone['SERVER_NAME'] )
409
+ return true;
410
+ }
411
+
412
+ if ( isset( $zone['server_names'] ) ) {
413
+ if ( !is_array( $zone['server_names'] ) )
414
+ die( 'server_names must be defined as array' );
415
+
416
+ foreach ( $zone['server_names'] as $server_name ) {
417
+ if ( $server_name == '*' )
418
+ return true;
419
+ if ( $_SERVER['SERVER_NAME'] == $server_name )
420
+ return true;
421
+ }
422
+ }
423
+
424
+ return false;
425
+ }
426
+
427
+ /*
428
+ * Tries to reuse opened connection
429
+ *
430
+ * @return resource
431
+ */
432
+ function _db_connect_reuse_connection() {
433
+ $dbhname = $this->dbhname;
434
+
435
+ if ( !isset( $this->_connections[$dbhname] ) )
436
+ return null;
437
+
438
+ $connection = & $this->_connections[$dbhname];
439
+ $dbh = $connection['dbh'];
440
+
441
+ if ( !is_resource( $dbh ) )
442
+ return null;
443
+
444
+ if ( !mysql_ping( $dbh ) ) {
445
+ // disconnect (ping failed)
446
+ $this->_disconnect( $dbhname );
447
+ return null;
448
+ }
449
+
450
+ return $dbh;
451
+ }
452
+
453
+ /**
454
+ * Sets the connection's character set.
455
+ *
456
+ * @param resource $dbh The resource given by mysql_connect
457
+ * @param string $charset The character set (optional)
458
+ * @param string $collate The collation (optional)
459
+ */
460
+ function set_charset( $dbh, $charset = null, $collate = null ) {
461
+ if ( !isset( $charset ) )
462
+ $charset = $this->wpdb_mixin->charset;
463
+ if ( !isset( $collate ) )
464
+ $collate = $this->wpdb_mixin->collate;
465
+ if ( $this->has_cap( 'collation', $dbh ) && !empty( $charset ) ) {
466
+ if ( function_exists( 'mysql_set_charset' ) && $this->has_cap( 'set_charset', $dbh ) ) {
467
+ mysql_set_charset( $charset, $dbh );
468
+ $this->wpdb_mixin->real_escape = true;
469
+ } else {
470
+ $query = $this->wpdb_mixin->prepare( 'SET NAMES %s', $charset );
471
+ if ( !empty( $collate ) )
472
+ $query .= $this->wpdb_mixin->prepare( ' COLLATE %s', $collate );
473
+ mysql_query( $query, $dbh );
474
+ }
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Kill cached query results
480
+ */
481
+ function flush() {
482
+ $this->wpdb_mixin->last_error = '';
483
+ $this->num_rows = 0;
484
+ $this->next_injection->flush();
485
+ }
486
+
487
+ /**
488
+ * Basic query. See docs for more details.
489
+ *
490
+ * @param string $query
491
+ * @return int number of rows
492
+ */
493
+ function query( $query ) {
494
+ // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method
495
+ if ( function_exists( 'apply_filters' ) )
496
+ $query = apply_filters( 'query', $query );
497
+
498
+ $this->flush();
499
+
500
+ // Log how the function was called
501
+ $this->wpdb_mixin->func_call = "\$db->query(\"$query\")";
502
+
503
+ // Keep track of the last query for debug..
504
+ $this->wpdb_mixin->last_query = $query;
505
+
506
+ if ( preg_match( '/^\s*SELECT\s+FOUND_ROWS(\s*)/i', $query )
507
+ && is_resource( $this->wpdb_mixin->_last_found_rows_result ) ) {
508
+ $this->wpdb_mixin->result = $this->wpdb_mixin->_last_found_rows_result;
509
+ $elapsed = 0;
510
+ } else {
511
+ $this->db_connect( $query );
512
+ if ( !is_resource( $this->wpdb_mixin->dbh ) )
513
+ return false;
514
+
515
+ $this->wpdb_mixin->timer_start();
516
+ $this->wpdb_mixin->result = mysql_query( $query, $this->wpdb_mixin->dbh );
517
+ $elapsed = $this->wpdb_mixin->timer_stop();
518
+ ++$this->wpdb_mixin->num_queries;
519
+
520
+ if ( preg_match( '/^\s*SELECT\s+SQL_CALC_FOUND_ROWS\s/i', $query ) ) {
521
+ if ( false === strpos( $query, "NO_SELECT_FOUND_ROWS" ) ) {
522
+ $this->wpdb_mixin->timer_start();
523
+ $this->wpdb_mixin->_last_found_rows_result = mysql_query( "SELECT FOUND_ROWS()", $this->wpdb_mixin->dbh );
524
+ $elapsed += $this->wpdb_mixin->timer_stop();
525
+ ++$this->wpdb_mixin->num_queries;
526
+ $query .= "; SELECT FOUND_ROWS()";
527
+ }
528
+ } else {
529
+ $this->wpdb_mixin->_last_found_rows_result = null;
530
+ }
531
+ }
532
+
533
+ if ( function_exists( 'apply_filters' ) )
534
+ apply_filters( 'after_query', $query );
535
+
536
+ // If there is an error then take note of it
537
+ if ( $this->wpdb_mixin->last_error = mysql_error( $this->wpdb_mixin->dbh ) ) {
538
+ $this->wpdb_mixin->print_error( $this->wpdb_mixin->last_error );
539
+ return false;
540
+ }
541
+
542
+ if ( preg_match( "/^\\s*(insert|delete|update|replace|alter) /i", $query ) ) {
543
+ $this->wpdb_mixin->rows_affected = mysql_affected_rows( $this->wpdb_mixin->dbh );
544
+
545
+ // Take note of the insert_id
546
+ if ( preg_match( "/^\\s*(insert|replace) /i", $query ) ) {
547
+ $this->wpdb_mixin->insert_id = mysql_insert_id( $this->wpdb_mixin->dbh );
548
+ }
549
+ // Return number of rows affected
550
+ return $this->wpdb_mixin->rows_affected;
551
+ } else {
552
+ $i = 0;
553
+ $this->wpdb_mixin->col_info = array();
554
+ $col_info = array();
555
+ while ( $i < @mysql_num_fields( $this->wpdb_mixin->result ) ) {
556
+ $col_info[$i] = @mysql_fetch_field( $this->wpdb_mixin->result );
557
+ $i++;
558
+ }
559
+ $this->wpdb_mixin->col_info = $col_info;
560
+ $num_rows = 0;
561
+ $this->wpdb_mixin->last_result = array();
562
+ while ( $row = @mysql_fetch_object( $this->wpdb_mixin->result ) ) {
563
+ $this->wpdb_mixin->last_result[$num_rows] = $row;
564
+ $num_rows++;
565
+ }
566
+
567
+ @mysql_free_result( $this->wpdb_mixin->result );
568
+
569
+ // Log number of rows the query returned
570
+ $this->num_rows = $num_rows;
571
+
572
+ // Return number of rows selected
573
+ return $num_rows;
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Whether or not MySQL database is at least the required minimum version.
579
+ * The additional argument allows the caller to check a specific database.
580
+ *
581
+ * @param string $dbh_or_table
582
+ * @return WP_Error
583
+ */
584
+ function check_database_version( $dbh_or_table = false ) {
585
+ global $wp_version;
586
+ // Make sure the server has MySQL 4.1.2
587
+ $mysql_version = preg_replace( '|[^0-9\.]|', '', $this->wpdb_mixin->db_version( $dbh_or_table ) );
588
+ if ( version_compare( $mysql_version, '4.1.2', '<' ) )
589
+ return new \WP_Error( 'database_version', sprintf( __( '<strong>ERROR</strong>: WordPress %s requires MySQL 4.1.2 or higher' ), $wp_version ) );
590
+ }
591
+
592
+ /**
593
+ * This function is called when WordPress is generating the table schema to determine wether or not the current database
594
+ * supports or needs the collation statements.
595
+ * The additional argument allows the caller to check a specific database.
596
+ *
597
+ * @param string $dbh_or_table
598
+ * @return bool
599
+ */
600
+ function supports_collation( $dbh_or_table = false ) {
601
+ return $this->wpdb_mixin->has_cap( 'collation', $dbh_or_table );
602
+ }
603
+
604
+ /**
605
+ * Generic function to determine if a database supports a particular feature
606
+ * The additional argument allows the caller to check a specific database.
607
+ *
608
+ * @param string $db_cap the feature
609
+ * @param bool|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource)
610
+ * @return bool
611
+ */
612
+ function has_cap( $db_cap, $dbh_or_table = false ) {
613
+ $version = $this->wpdb_mixin->db_version( $dbh_or_table );
614
+
615
+ switch ( strtolower( $db_cap ) ) :
616
+ case 'collation' :
617
+ case 'group_concat' :
618
+ case 'subqueries' :
619
+ return version_compare( $version, '4.1', '>=' );
620
+ case 'set_charset' :
621
+ return version_compare( $version, '5.0.7', '>=' );
622
+ endswitch;
623
+
624
+ return false;
625
+ }
626
+
627
+ /**
628
+ * The database version number
629
+ *
630
+ * @param false|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource)
631
+ * @return false|string false on failure, version number on success
632
+ */
633
+ function db_version( $dbh_or_table = false ) {
634
+ if ( !$dbh_or_table && $this->wpdb_mixin->dbh )
635
+ $dbh = $this->wpdb_mixin->dbh;
636
+ elseif ( is_resource( $dbh_or_table ) )
637
+ $dbh = $dbh_or_table;
638
+ else
639
+ $dbh = $this->db_connect( "SELECT FROM $dbh_or_table $this->wpdb_mixin->users" );
640
+
641
+ if ( $dbh )
642
+ return preg_replace( '/[^0-9.].*/', '', mysql_get_server_info( $dbh ) );
643
+ return false;
644
+ }
645
+
646
+ /**
647
+ * Disconnect and remove connection from open connections list
648
+ *
649
+ * @param string $dbhname
650
+ */
651
+ function _disconnect( $dbhname ) {
652
+ if ( isset( $this->_connections[$dbhname] ) ) {
653
+ $dbh = $this->_connections[$dbhname]['dbh'];
654
+ if ( is_resource( $dbh ) )
655
+ mysql_close( $dbh );
656
+
657
+ unset( $this->_connections[$dbhname] );
658
+ }
659
+ }
660
+
661
+ /**
662
+ * Check the responsiveness of a tcp/ip daemon
663
+ *
664
+ * @return (bool) true when $host:$post responds within $float_timeout seconds, else (bool) false
665
+ */
666
+ function _check_tcp_responsiveness( $host, $port, $float_timeout ) {
667
+ $socket = @ fsockopen( $host, $port, $errno, $errstr, $float_timeout );
668
+ if ( $socket === false )
669
+ return "[ > $float_timeout ] ($errno) '$errstr'";
670
+ fclose( $socket );
671
+ return true;
672
+ }
673
+
674
+ /**
675
+ * Find the first table name referenced in a query
676
+ *
677
+ * @param string $q
678
+ * @return string table
679
+ */
680
+ function _get_table_from_query( $q ) {
681
+ // Remove characters that can legally trail the table name
682
+ $q = rtrim( $q, ';/-#' );
683
+ // allow (select...) union [...] style queries. Use the first queries table name.
684
+ $q = ltrim( $q, "\t (" );
685
+
686
+ // Quickly match most common queries
687
+ if ( preg_match( '/^\s*(?:'
688
+ . 'SELECT.*?\s+FROM'
689
+ . '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?'
690
+ . '|REPLACE(?:\s+INTO)?'
691
+ . '|UPDATE(?:\s+IGNORE)?'
692
+ . '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?'
693
+ . ')\s+`?(\w+)`?/is', $q, $maybe ) )
694
+ return $maybe[1];
695
+
696
+ // Refer to the previous query
697
+ if ( preg_match( '/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q ) )
698
+ return $this->_last_table;
699
+
700
+ // SHOW TABLE STATUS LIKE and SHOW TABLE STATUS WHERE Name =
701
+ if ( preg_match( '/^\s*'
702
+ . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'
703
+ . '\W(\w+)\W/is', $q, $maybe ) )
704
+ return $maybe[1];
705
+
706
+ // Big pattern for the rest of the table-related queries in MySQL 5.0
707
+ if ( preg_match( '/^\s*(?:'
708
+ . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM'
709
+ . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?'
710
+ . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
711
+ . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
712
+ . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
713
+ . '|DESCRIBE|DESC|EXPLAIN|HANDLER'
714
+ . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?'
715
+ . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE'
716
+ . '|TRUNCATE(?:\s+TABLE)?'
717
+ . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?'
718
+ . '|ALTER(?:\s+IGNORE)?\s+TABLE'
719
+ . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?'
720
+ . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON'
721
+ . '|DROP\s+INDEX.*\s+ON'
722
+ . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
723
+ . '|(?:GRANT|REVOKE).*ON\s+TABLE'
724
+ . '|SHOW\s+(?:.*FROM|.*TABLE)'
725
+ . ')\s+`?(\w+)`?/is', $q, $maybe ) )
726
+ return $maybe[1];
727
+ }
728
+
729
+ /**
730
+ * Determine the likelihood that this query could alter anything
731
+ *
732
+ * @param string $q
733
+ * @return bool
734
+ */
735
+ function _is_write_query( $q ) {
736
+ // Quick and dirty: only SELECT statements are considered read-only.
737
+ $q = ltrim( $q, "\r\n\t (" );
738
+ return!preg_match( '/^(?:SELECT|SHOW|DESCRIBE|EXPLAIN)\s/i', $q );
739
+ }
740
+
741
+ /**
742
+ * Callbacks are executed in the order in which they are registered until one
743
+ * of them returns something other than null.
744
+ *
745
+ * @param string $group
746
+ * @param array $args
747
+ */
748
+ function _run_callbacks( $group, $args = null ) {
749
+ if ( !isset( $this->_callbacks[$group] ) ||
750
+ !is_array( $this->_callbacks[$group] ) )
751
+ return null;
752
+
753
+ if ( !isset( $args ) ) {
754
+ $args = array( $this );
755
+ } elseif ( is_array( $args ) ) {
756
+ $args[] = $this;
757
+ } else {
758
+ $args = array( $args, $this );
759
+ }
760
+
761
+ foreach ( $this->_callbacks[$group] as $func ) {
762
+ $result = call_user_func_array( $func, $args );
763
+ if ( isset( $result ) )
764
+ return $result;
765
+ }
766
+ }
767
+
768
+ /**
769
+ * Connects to server using default configuration
770
+ *
771
+ * @return resource mysql database connection
772
+ */
773
+ function _db_connect_fallback() {
774
+ if ( is_resource( $this->wpdb_mixin->dbh ) )
775
+ return $this->wpdb_mixin->dbh;
776
+ if ( !defined( 'DB_HOST' )
777
+ || !defined( 'DB_USER' )
778
+ || !defined( 'DB_PASSWORD' )
779
+ || !defined( 'DB_NAME' ) )
780
+ return $this->wpdb_mixin->bail( "We were unable to query because there was no database defined." );
781
+
782
+ $connect_function = $this->persistent ? 'mysql_pconnect' : 'mysql_connect';
783
+ $this->wpdb_mixin->dbh = @ $connect_function( DB_HOST, DB_USER, DB_PASSWORD, true );
784
+
785
+ if ( !is_resource( $this->wpdb_mixin->dbh ) )
786
+ return $this->wpdb_mixin->bail( "We were unable to connect to the database. (DB_HOST)" );
787
+ if ( !mysql_select_db( DB_NAME, $this->wpdb_mixin->dbh ) )
788
+ return $this->wpdb_mixin->bail( "We were unable to select the database." );
789
+ if ( !empty( $this->wpdb_mixin->charset ) ) {
790
+ $collation_query = "SET NAMES '$this->wpdb_mixin->charset'";
791
+ if ( !empty( $this->wpdb_mixin->collate ) )
792
+ $collation_query .= " COLLATE '$this->wpdb_mixin->collate'";
793
+ mysql_query( $collation_query, $this->wpdb_mixin->dbh );
794
+ }
795
+
796
+ return $this->wpdb_mixin->dbh;
797
+ }
798
+
799
+ function w3tc_footer_comment( $strings ) {
800
+ $append = '';
801
+ if ( isset( $this->_current_zone['name'] ) && $this->_current_zone['name'] != 'all' )
802
+ $append .= ' with home zone ' . $this->_current_zone['name'];
803
+
804
+ if ( !is_null( $this->_reject_reason ) )
805
+ $append .= ' (' . $this->_reject_reason . ')';
806
+
807
+ $strings[] = 'Database cluster enabled ' . $append;
808
+ return $strings;
809
+ }
810
+
811
+ public function w3tc_usage_statistics_of_request( $storage ) {
812
+ }
813
+ }
Enterprise_SnsBase.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * Purge using AmazonSNS object
8
+ */
9
+
10
+ /**
11
+ * class Sns
12
+ */
13
+ class Enterprise_SnsBase {
14
+ /**
15
+ * PHP5-style constructor
16
+ */
17
+ function __construct() {
18
+ $this->_config = Dispatcher::config();
19
+
20
+ $this->_region = $this->_config->get_string( 'cluster.messagebus.sns.region' );
21
+ $this->_topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
22
+ $this->_api_key = $this->_config->get_string( 'cluster.messagebus.sns.api_key' );
23
+ $this->_api_secret = $this->_config->get_string( 'cluster.messagebus.sns.api_secret' );
24
+
25
+ $this->_debug = $this->_config->get_boolean( 'cluster.messagebus.debug' );
26
+ $this->_api = null;
27
+ }
28
+
29
+ /**
30
+ * Returns API object
31
+ *
32
+ * @throws Exception
33
+ * @return AmazonSNS
34
+ */
35
+ protected function _get_api() {
36
+ if ( is_null( $this->_api ) ) {
37
+ if ( $this->_api_key == '' )
38
+ throw new \Exception( 'API Key is not configured' );
39
+ if ( $this->_api_secret == '' )
40
+ throw new \Exception( 'API Secret is not configured' );
41
+
42
+ require_once W3TC_LIB_DIR . '/SNS/sdk.class.php';
43
+ $this->_api = new \AmazonSNS( $this->_api_key, $this->_api_secret );
44
+ if ( $this->_region != '' ) {
45
+ $this->_api->set_region( $this->_region );
46
+ }
47
+ }
48
+
49
+ return $this->_api;
50
+ }
51
+
52
+ /**
53
+ * Write log entry
54
+ *
55
+ * @param string $message
56
+ * @param array $backtrace
57
+ * @return bool|int
58
+ */
59
+ protected function _log( $message, $backtrace = null ) {
60
+ if ( !$this->_debug )
61
+ return true;
62
+
63
+ $data = sprintf( "[%s] %s\n", date( 'r' ), $message );
64
+ if ( $backtrace ) {
65
+ $debug = print_r( $backtrace, true );
66
+ $data .= $debug . "\n";
67
+ }
68
+ $data = strtr( $data, '<>', '..' );
69
+
70
+ $filename = Util_Debug::log_filename( 'sns' );
71
+
72
+ return @file_put_contents( $filename, $data, FILE_APPEND );
73
+ }
74
+ }
Enterprise_SnsServer.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Purge using AmazonSNS object
6
+ */
7
+
8
+ require_once W3TC_LIB_DIR . '/SNS/services/MessageValidator/Message.php';
9
+ require_once W3TC_LIB_DIR . '/SNS/services/MessageValidator/MessageValidator.php';
10
+
11
+ /**
12
+ * class Sns
13
+ */
14
+ class Enterprise_SnsServer extends Enterprise_SnsBase {
15
+
16
+ /**
17
+ * Processes message from SNS
18
+ *
19
+ * @throws Exception
20
+ */
21
+ function process_message() {
22
+ $this->_log( 'Received message' );
23
+
24
+ try {
25
+ $message = \Message::fromRawPostData();
26
+ $validator = new \MessageValidator();
27
+ $error = '';
28
+ if ( $validator->isValid( $message ) ) {
29
+ $topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
30
+
31
+ if ( empty( $topic_arn ) || $topic_arn != $message->get( 'TopicArn' ) )
32
+ throw new \Exception ( 'Not my Topic. Request came from ' .
33
+ $message->get( 'TopicArn' ) );
34
+
35
+ if ( $message->get( 'Type' ) == 'SubscriptionConfirmation' )
36
+ $this->_subscription_confirmation( $message );
37
+ elseif ( $message->get( 'Type' ) == 'Notification' )
38
+ $this->_notification( $message->get( 'Message' ) );
39
+ } else {
40
+ $this->_log( 'Error processing message it was not valid.' );
41
+ }
42
+ } catch ( \Exception $e ) {
43
+ $this->_log( 'Error processing message: ' . $e->getMessage() );
44
+ }
45
+ $this->_log( 'Message processed' );
46
+ }
47
+
48
+ /**
49
+ * Confirms subscription
50
+ *
51
+ * @param Message $message
52
+ * @throws Exception
53
+ */
54
+ private function _subscription_confirmation( $message ) {
55
+ $this->_log( 'Issuing confirm_subscription' );
56
+ $response = $this->_get_api()->confirm_subscription(
57
+ $topic_arn, $message->get( 'Token' ) );
58
+ $this->_log( 'Subscription confirmed: ' .
59
+ ( $response->isOK() ? 'OK' : 'Error' ) );
60
+ }
61
+
62
+ /**
63
+ * Processes notification
64
+ *
65
+ * @param array $v
66
+ */
67
+ private function _notification( $v ) {
68
+ $m = json_decode( $v, true );
69
+ if ( isset( $m['hostname'] ) )
70
+ $this->_log( 'Message originated from hostname: ' . $m['hostname'] );
71
+
72
+ define( 'DOING_SNS', true );
73
+ $this->_log( 'Actions executing' );
74
+ do_action( 'w3tc_messagebus_message_received' );
75
+
76
+ if ( isset( $m['actions'] ) ) {
77
+ $actions = $m['actions'];
78
+ foreach ( $actions as $action )
79
+ $this->_execute( $action );
80
+ } else {
81
+ $this->_execute( $m['action'] );
82
+ }
83
+
84
+ do_action( 'w3tc_messagebus_message_processed' );
85
+ $this->_log( 'Actions executed' );
86
+ }
87
+
88
+ /**
89
+ * Execute action
90
+ *
91
+ * @param unknown $m
92
+ * @throws Exception
93
+ */
94
+ private function _execute( $m ) {
95
+ $action = $m['action'];
96
+ $this->_log( 'Executing action ' . $action );
97
+ //Needed for cache flushing
98
+ $executor = new CacheFlush_Locally();
99
+ //Needed for cache cleanup
100
+ $pgcache_admin = Dispatcher::component( 'PgCache_Plugin_Admin' );
101
+
102
+ //See which message we got
103
+ if ( $action == 'dbcache_flush' )
104
+ $executor->dbcache_flush();
105
+ elseif ( $action == 'objectcache_flush' )
106
+ $executor->objectcache_flush();
107
+ elseif ( $action == 'fragmentcache_flush' )
108
+ $executor->fragmentcache_flush();
109
+ elseif ( $action == 'fragmentcache_flush_group' )
110
+ $executor->fragmentcache_flush_group( $m['group'] );
111
+ elseif ( $action == 'minifycache_flush' )
112
+ $executor->minifycache_flush();
113
+ elseif ( $action == 'browsercache_flush' )
114
+ $executor->browsercache_flush();
115
+ elseif ( $action == 'cdn_purge_files' )
116
+ $executor->cdn_purge_files( $m['purgefiles'] );
117
+ elseif ( $action == 'pgcache_cleanup' )
118
+ $pgcache_admin->cleanup_local();
119
+ elseif ( $action == 'opcache_flush' )
120
+ $executor->opcache_flush();
121
+ elseif ( $action == 'opcache_flush_file' )
122
+ $executor->opcache_flush_file( $m['filename'] );
123
+ elseif ( $action == 'flush_all' )
124
+ $executor->flush_all(
125
+ isset( $m['extras'] ) ? $m['extras'] : null );
126
+ elseif ( $action == 'flush_post' )
127
+ $executor->flush_post( $m['post_id'] );
128
+ elseif ( $action == 'flush_url' )
129
+ $executor->flush_url( $m['url'] );
130
+ elseif ( $action == 'prime_post' )
131
+ $executor->prime_post( $m['post_id'] );
132
+ else
133
+ throw new \Exception( 'Unknown action ' . $action );
134
+
135
+ $this->_log( 'succeeded' );
136
+ }
137
+ }
Extension_CloudFlare_AdminActions.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_CloudFlare_AdminActions {
7
+ public function w3tc_cloudflare_flush() {
8
+ $c = Dispatcher::config();
9
+ $api = new Extension_CloudFlare_Api( array(
10
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
11
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) ),
12
+ 'zone_id' => $c->get_string( array( 'cloudflare', 'zone_id' ) ),
13
+ 'timelimit_api_request' => $c->get_integer(
14
+ array( 'cloudflare', 'timelimit.api_request' ) )
15
+ )
16
+ );
17
+
18
+ try {
19
+ $v = $api->purge();
20
+ } catch ( \Exception $ex ) {
21
+ Util_Admin::redirect_with_custom_messages2( array(
22
+ 'errors' => array(
23
+ 'cloudflare_flush' =>
24
+ __( 'Failed to flush CloudFlare cache: ', 'w3-total-cache' ) .
25
+ $ex->getMessage()
26
+ )
27
+ ) );
28
+ return;
29
+ }
30
+
31
+ Util_Admin::redirect_with_custom_messages2( array(
32
+ 'notes' => array(
33
+ 'cloudflare_flush' => __( 'CloudFlare cache successfully emptied.', 'w3-total-cache' )
34
+ )
35
+ ) );
36
+ }
37
+
38
+
39
+
40
+ /**
41
+ * Flush all caches except CloudFlare action
42
+ *
43
+ * @return void
44
+ */
45
+ public function w3tc_cloudflare_flush_all_except_cf() {
46
+ Dispatcher::component( 'CacheFlush' )->flush_all( array(
47
+ 'cloudflare' => 'skip' ) );
48
+
49
+ Util_Admin::redirect( array(
50
+ 'w3tc_note' => 'flush_all'
51
+ ), true );
52
+ }
53
+
54
+
55
+
56
+ public function w3tc_cloudflare_save_settings() {
57
+ $api = Extension_CloudFlare_SettingsForUi::api();
58
+ $errors = Extension_CloudFlare_SettingsForUi::settings_set( $api );
59
+
60
+ if ( empty( $errors ) ) {
61
+ Util_Admin::redirect_with_custom_messages2( array(
62
+ 'notes' => array(
63
+ 'cloudflare_save_done' =>
64
+ __( 'CloudFlare settings are successfully updated.',
65
+ 'w3-total-cache' )
66
+ )
67
+ ) );
68
+ } else {
69
+ Util_Admin::redirect_with_custom_messages2( array(
70
+ 'errors' => array(
71
+ 'cloudflare_save_error' =>
72
+ __( 'Failed to update CloudFlare settings:',
73
+ 'w3-total-cache' ) .
74
+ "<br />\n" .
75
+ implode( "<br />\n", $errors )
76
+ )
77
+ ) );
78
+ }
79
+ }
80
+ }
Extension_CloudFlare_Api.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * CloudFlare API
8
+ */
9
+ class Extension_CloudFlare_Api {
10
+ static private $_root_uri = 'https://api.cloudflare.com/client/v4';
11
+
12
+ private $_email;
13
+ private $_key;
14
+ private $_zone_id;
15
+ private $_timelimit_api_request;
16
+
17
+
18
+
19
+ function __construct( $config ) {
20
+ $this->_email = $config['email'];
21
+ $this->_key = $config['key'];
22
+ $this->_zone_id =
23
+ ( isset( $config['zone_id'] ) ? $config['zone_id'] : '' );
24
+
25
+ if ( !isset( $config['timelimit_api_request'] ) ||
26
+ $config['timelimit_api_request'] < 1 )
27
+ $this->_timelimit_api_request = 30;
28
+ else
29
+ $this->_timelimit_api_request = $config['timelimit_api_request'];
30
+ }
31
+
32
+
33
+
34
+ /**
35
+ * Makes ip_lkup API request
36
+ *
37
+ * @param string $ip
38
+ * @return object
39
+ */
40
+ public function ip_lkup( $ip ) {
41
+ @set_time_limit( $this->_timelimit_api_request );
42
+
43
+ $response = Util_Http::request(
44
+ 'https://www.cloudflare.com/api_json.html', array(
45
+ 'method' => 'POST',
46
+ 'body' => array(
47
+ 'a' => 'ip_lkup',
48
+ 'tkn' => $this->_key,
49
+ 'email' => $this->_email,
50
+ 'ip' => $ip
51
+ )
52
+ ) );
53
+ if ( isset( $response['body'] ) )
54
+ return @json_decode( $response['body'] );
55
+
56
+ return null;
57
+ }
58
+
59
+
60
+
61
+ /**
62
+ * Makes external event request
63
+ *
64
+ * @param string $type
65
+ * @param string $value
66
+ * @return array
67
+ */
68
+ public function external_event( $type, $value ) {
69
+ $url = sprintf( 'https://www.cloudflare.com/ajax/external-event.html?' .
70
+ 'u=%s&tkn=%s&evnt_t=%s&evnt_v=%s',
71
+ urlencode( $this->_email ), urlencode( $this->_key ),
72
+ urlencode( $type ), urlencode( $value ) );
73
+ $response = Util_Http::get( $url );
74
+
75
+ if ( !is_wp_error( $response ) ) {
76
+ return json_decode( $response['body'] );
77
+ }
78
+
79
+ return null;
80
+ }
81
+
82
+
83
+ /**
84
+ * Check
85
+ *
86
+ * @throws Util_WpFile_FilesystemOperationException
87
+ * @throws FileOperationException
88
+ */
89
+ public function get_ip_ranges() {
90
+ $data = array();
91
+ $response = Util_Http::get( 'https://www.cloudflare.com/ips-v4' );
92
+
93
+ if ( !is_wp_error( $response ) ) {
94
+ $ip4_data = $response['body'];
95
+ $ip4_data = explode( "\n", $ip4_data );
96
+ $data['ip4'] = $ip4_data;
97
+ }
98
+ $response = Util_Http::get( 'https://www.cloudflare.com/ips-v6' );
99
+ if ( !is_wp_error( $response ) ) {
100
+ $ip6_data = $response['body'];
101
+ $ip6_data = explode( "\n", $ip6_data );
102
+ $data['ip6'] = $ip6_data;
103
+ }
104
+
105
+ return $data;
106
+ }
107
+
108
+
109
+
110
+ public function zones() {
111
+ return $this->_wp_remote_request( 'GET',
112
+ self::$_root_uri . '/zones' );
113
+ }
114
+
115
+
116
+
117
+ public function zone_settings() {
118
+ $a = $this->_wp_remote_request( 'GET',
119
+ self::$_root_uri . '/zones/' . $this->_zone_id . '/settings' );
120
+
121
+ $by_id = array();
122
+ foreach ( $a as $i ) {
123
+ $by_id[$i['id']] = $i;
124
+ }
125
+
126
+ return $by_id;
127
+ }
128
+
129
+
130
+
131
+ public function zone_setting_set( $name, $value ) {
132
+ return $this->_wp_remote_request( 'PATCH',
133
+ self::$_root_uri . '/zones/' . $this->_zone_id . '/settings/' . $name,
134
+ json_encode( array( 'value' => $value ) ) );
135
+ }
136
+
137
+
138
+
139
+ public function analytics_dashboard( $interval ) {
140
+ return $this->_wp_remote_request( 'GET',
141
+ self::$_root_uri . '/zones/' . $this->_zone_id .
142
+ '/analytics/dashboard' );
143
+ }
144
+
145
+
146
+
147
+
148
+ public function purge() {
149
+ return $this->_wp_remote_request( 'DELETE',
150
+ self::$_root_uri . '/zones/' . $this->_zone_id . '/purge_cache',
151
+ '{"purge_everything":true}' );
152
+ }
153
+
154
+
155
+
156
+ private function _wp_remote_request( $method, $url, $body = array() ) {
157
+ $result = wp_remote_request( $url, array(
158
+ 'method' => $method,
159
+ 'headers' => array(
160
+ 'Content-Type' => 'application/json',
161
+ 'X-Auth-Key' => $this->_key,
162
+ 'X-Auth-Email' => $this->_email
163
+ ),
164
+ 'timeout' => $this->_timelimit_api_request,
165
+ 'body' => $body
166
+ ) );
167
+
168
+ if ( is_wp_error( $result ) )
169
+ throw new \Exception( 'Failed to reach API endpoint' );
170
+
171
+ $response_json = @json_decode( $result['body'], true );
172
+ if ( is_null( $response_json ) || !isset( $response_json['success'] ) ) {
173
+ throw new \Exception(
174
+ 'Failed to reach API endpoint, got unexpected response ' .
175
+ $result['body'] );
176
+ }
177
+
178
+ if ( !$response_json['success'] ) {
179
+ $errors = array();
180
+ if ( isset( $response_json['errors'] ) ) {
181
+ foreach ( $response_json['errors'] as $e ) {
182
+ if ( !empty( $e['message'] ) )
183
+ $errors[] = $e['message'];
184
+ }
185
+ }
186
+
187
+ if ( empty( $errors ) )
188
+ $errors[] = 'Request failed';
189
+
190
+ throw new \Exception( implode( ', ', $errors ) );
191
+ }
192
+
193
+ if ( isset( $response_json['result'] ) ) {
194
+ return $response_json['result'];
195
+ }
196
+
197
+ return array();
198
+ }
199
+ }
Extension_CloudFlare_GeneralPage_View.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php Util_Ui::postbox_header( __( 'Network Performance &amp; Security powered by CloudFlare', 'w3-total-cache' ), '', 'cloudflare' ); ?>
9
+ <?php
10
+ Util_Ui::config_overloading_button( array(
11
+ 'key' => 'cloudflare.configuration_overloaded'
12
+ ) );
13
+ ?>
14
+ <p>
15
+ <?php _e( 'CloudFlare protects and accelerates websites.', 'w3-total-cache' ) ?>
16
+ </p>
17
+
18
+ <table class="form-table">
19
+ <?php
20
+ Util_Ui::config_item( array(
21
+ 'key' => array( 'cloudflare', 'widget_cache_mins' ),
22
+ 'label' => __( 'Cache time:', 'w3-total-cache' ),
23
+ 'control' => 'textbox',
24
+ 'description' =>
25
+ 'How many minutes data retrieved from CloudFlare ' .
26
+ 'should be stored. Minimum is 1 minute.'
27
+ ) );
28
+
29
+ Util_Ui::config_item( array(
30
+ 'key' => array( 'cloudflare', 'pagecache' ),
31
+ 'label' => __( 'Page Caching:', 'w3-total-cache' ),
32
+ 'control' => 'checkbox',
33
+ 'checkbox_label' => 'Flush CloudFlare on Post Modifications',
34
+ 'description' =>
35
+ 'Enable when you have html pages cached on CloudFlare level.'
36
+ ) );
37
+ ?>
38
+ </table>
39
+
40
+ <?php
41
+ Util_Ui::button_config_save( 'general_cloudflare',
42
+ '<input type="submit" name="w3tc_cloudflare_flush" value="' .
43
+ __( 'Empty cache', 'w3-total-cache' ) . '"' .
44
+ ' class="button" />' );
45
+ ?>
46
+ <?php Util_Ui::postbox_footer(); ?>
Extension_CloudFlare_Page.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_CloudFlare_Page {
5
+ static public function admin_print_scripts_w3tc_extensions() {
6
+ if ( isset( $_REQUEST['extension'] ) &&
7
+ $_REQUEST['extension'] == 'cloudflare' ) {
8
+ wp_enqueue_script( 'w3tc_extension_cloudflare',
9
+ plugins_url( 'Extension_CloudFlare_Page_View.js', W3TC_FILE ),
10
+ array( 'jquery' ), '1.0' );
11
+ }
12
+ }
13
+
14
+
15
+
16
+ static public function w3tc_extension_page_cloudflare() {
17
+ $c = Dispatcher::config();
18
+ $api = Extension_CloudFlare_SettingsForUi::api();
19
+
20
+ $email = $c->get_string( array( 'cloudflare', 'email' ) );
21
+ $key = $c->get_string( array( 'cloudflare', 'key' ) );
22
+ $zone_id = $c->get_string( array( 'cloudflare', 'zone_id' ) );
23
+
24
+ if ( empty( $email ) || empty( $key ) || empty( $zone_id ) ) {
25
+ $state = 'not_configured';
26
+ } else {
27
+ $settings = array();
28
+
29
+ try {
30
+ $settings =
31
+ Extension_CloudFlare_SettingsForUi::settings_get( $api );
32
+ $state = 'available';
33
+ } catch ( \Exception $ex ) {
34
+ $state = 'not_available';
35
+ $error_message = $ex->getMessage();
36
+
37
+ }
38
+ }
39
+
40
+ $config = $c;
41
+ include W3TC_DIR . '/Extension_CloudFlare_Page_View.php';
42
+ }
43
+
44
+
45
+
46
+ static private function cloudflare_checkbox( $settings, $data ) {
47
+ if ( !isset( $settings[$data['key']] ) )
48
+ return;
49
+
50
+ $value = ( $settings[$data['key']]['value'] == 'on' );
51
+ $disabled = !$settings[$data['key']]['editable'];
52
+
53
+ Util_Ui::table_tr( array(
54
+ 'id' => $data['key'],
55
+ 'label' => $data['label'],
56
+ 'checkbox' => array(
57
+ 'name' => 'cloudflare_api_' . $data['key'],
58
+ 'value' => $value,
59
+ 'disabled' => $disabled,
60
+ 'label' => 'Enable'
61
+ ),
62
+ 'description' => $data['description']
63
+ ) );
64
+ }
65
+
66
+
67
+
68
+ static private function cloudflare_selectbox( $settings, $data ) {
69
+ if ( !isset( $settings[$data['key']] ) )
70
+ return;
71
+
72
+ $value = $settings[$data['key']]['value'];
73
+ $disabled = !$settings[$data['key']]['editable'];
74
+
75
+ Util_Ui::table_tr( array(
76
+ 'id' => $data['key'],
77
+ 'label' => $data['label'],
78
+ 'selectbox' => array(
79
+ 'name' => 'cloudflare_api_' . $data['key'],
80
+ 'value' => $value,
81
+ 'disabled' => $disabled,
82
+ 'values' => $data['values']
83
+ ),
84
+ 'description' => $data['description']
85
+ ) );
86
+ }
87
+
88
+
89
+
90
+ static private function cloudflare_textbox( $settings, $data ) {
91
+ if ( !isset( $settings[$data['key']] ) )
92
+ return;
93
+
94
+ $value = $settings[$data['key']]['value'];
95
+ $disabled = !$settings[$data['key']]['editable'];
96
+
97
+ Util_Ui::table_tr( array(
98
+ 'id' => $data['key'],
99
+ 'label' => $data['label'],
100
+ 'textbox' => array(
101
+ 'name' => 'cloudflare_api_' . $data['key'],
102
+ 'value' => $value,
103
+ 'disabled' => $disabled
104
+ ),
105
+ 'description' => $data['description']
106
+ ) );
107
+ }
108
+
109
+
110
+
111
+ static private function cloudflare_button_save( $id = '' ) {
112
+ $b1_id = 'w3tc_cloudflare_save_' . $id;
113
+
114
+ echo '<p class="submit">';
115
+ echo Util_Ui::nonce_field( 'w3tc' );
116
+ echo '<input type="submit" id="' . $b1_id .
117
+ '" name="w3tc_cloudflare_save_settings" ' .
118
+ ' class="w3tc-button-save button-primary" '.
119
+ ' value="' . __( 'Save CloudFlare settings', 'w3-total-cache' ) .
120
+ '" />';
121
+ echo '</p>';
122
+ }
123
+ }
Extension_CloudFlare_Page_View.js ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ function w3tc_extension_cloudflare_resize(o) {
3
+ o.options.height = jQuery('.w3tc_extension_cloudflare_form').height() + 30;
4
+ o.resize();
5
+ }
6
+
7
+
8
+ $('body')
9
+ /**
10
+ * Authorize popup
11
+ */
12
+ .on('click', '.w3tc_extension_cloudflare_authorize', function() {
13
+ W3tc_Lightbox.open({
14
+ id:'w3tc-overlay',
15
+ close: '',
16
+ width: 800,
17
+ height: 300,
18
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
19
+ '&w3tc_action=extension_cloudflare_intro',
20
+ callback: w3tc_extension_cloudflare_resize
21
+ });
22
+ })
23
+
24
+
25
+
26
+ .on('click', '.w3tc_popup_submit', function() {
27
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce;
28
+
29
+ W3tc_Lightbox.load_form(url, '.w3tc_extension_cloudflare_form',
30
+ w3tc_extension_cloudflare_resize);
31
+ })
32
+ });
Extension_CloudFlare_Page_View.php ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <p id="w3tc-options-menu">
8
+ Jump to:
9
+ <a href="admin.php?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a> |
10
+ <a href="admin.php?page=w3tc_extensions"><?php _e( 'Extensions', 'w3-total-cache' ); ?></a> |
11
+ <a href="#credentials"><?php _e( 'Credentials', 'w3-total-cache' ); ?></a> |
12
+ <a href="#general"><?php _e( 'General', 'w3-total-cache' ); ?></a> |
13
+ <a href="#info"><?php _e( 'Information', 'w3-total-cache' ); ?></a>
14
+ </p>
15
+ <p>
16
+ CloudFlare extension is currently <?php
17
+ if ( $config->is_extension_active_frontend( 'cloudflare' ) )
18
+ echo '<span class="w3tc-enabled">enabled</span>';
19
+ else
20
+ echo '<span class="w3tc-disabled">disabled</span>';
21
+ ?>.
22
+ <p>
23
+
24
+ <form action="admin.php?page=w3tc_extensions&amp;extension=cloudflare&amp;action=view" method="post">
25
+ <p>
26
+ <?php echo Util_Ui::nonce_field( 'w3tc' ); ?>
27
+ <input type="submit" name="w3tc_cloudflare_flush" value="<?php _e( 'Purge CloudFlare cache', 'w3-total-cache' ) ?>" class="button" />
28
+ <?php _e( 'if needed.', 'w3-total-cache' ) ?>
29
+ </p>
30
+ </form>
31
+
32
+ <form action="admin.php?page=w3tc_extensions&amp;extension=cloudflare&amp;action=view" method="post">
33
+ <div class="metabox-holder">
34
+ <?php Util_Ui::postbox_header( __( 'Credentials', 'w3-total-cache' ), '', 'credentials' ); ?>
35
+ <table class="form-table">
36
+ <tr>
37
+ <th style="width: 300px;">
38
+ <label>
39
+ <?php
40
+ _e( 'Specify account credentials:',
41
+ 'w3-total-cache' );
42
+ ?>
43
+ </label>
44
+ </th>
45
+ <td>
46
+ <?php if ( $state != 'not_configured' ): ?>
47
+ <input class="w3tc_extension_cloudflare_authorize button-primary"
48
+ type="button"
49
+ value="<?php _e( 'Reauthorize', 'w3-total-cache' ); ?>"
50
+ />
51
+ <?php else: ?>
52
+ <input class="w3tc_extension_cloudflare_authorize button-primary"
53
+ type="button"
54
+ value="<?php _e( 'Authorize', 'w3-total-cache' ); ?>"
55
+ />
56
+ <?php endif ?>
57
+ </td>
58
+ </tr>
59
+
60
+ <?php if ( $state != 'not_configured' ): ?>
61
+ <tr>
62
+ <th>
63
+ <label><?php _e( 'Zone:', 'w3-total-cache' ); ?></label>
64
+ </th>
65
+ <td class="w3tc_config_value_text">
66
+ <?php echo $config->get_string( array( 'cloudflare', 'zone_name' ) ) ?>
67
+ </td>
68
+ </tr>
69
+ <?php endif ?>
70
+ </table>
71
+
72
+
73
+ <?php Util_Ui::postbox_footer(); ?>
74
+ <?php Util_Ui::postbox_header( __( 'General', 'w3-total-cache' ), '', 'general' ); ?>
75
+
76
+
77
+ <?php if ( $state == 'not_configured' ): ?>
78
+ <table class="form-table">
79
+ <tr><td colspan="2">
80
+ CloudFlare credentials not specified yet
81
+ </td></tr>
82
+ </table>
83
+ <?php endif ?>
84
+
85
+
86
+ <?php if ( $state == 'not_available' ): ?>
87
+ <table class="form-table">
88
+ <tr><td colspan="2">
89
+ CloudFlare not available: <?php echo $error_message; ?>
90
+ </td></tr>
91
+ </table>
92
+ <?php endif ?>
93
+
94
+
95
+ <?php if ( $state == 'available' ): ?>
96
+ <table class="form-table">
97
+ <?php
98
+
99
+ Util_Ui::config_item( array(
100
+ 'key' => array( 'cloudflare', 'widget_interval' ),
101
+ 'label' => __( 'Widget statistics interval:', 'w3-total-cache' ),
102
+ 'control' => 'selectbox',
103
+ 'selectbox_values' => array(
104
+ '-30' => 'Last 30 minutes',
105
+ '-360' => 'Last 6 hours',
106
+ '-720' => 'Last 12 hours',
107
+ '-1440' => 'Last 24 hours',
108
+ '-10080' => 'Last week',
109
+ '-43200' => 'Last month'
110
+ )
111
+ )
112
+ );
113
+
114
+ Util_Ui::config_item( array(
115
+ 'key' => array( 'cloudflare', 'widget_cache_mins' ),
116
+ 'label' => __( 'Cache time:', 'w3-total-cache' ),
117
+ 'control' => 'textbox',
118
+ 'description' =>
119
+ 'How many minutes data retrieved from CloudFlare ' .
120
+ 'should be stored. Minimum is 1 minute.'
121
+ )
122
+ );
123
+
124
+ Util_Ui::config_item( array(
125
+ 'key' => array( 'cloudflare', 'pagecache' ),
126
+ 'label' => __( 'Page Caching:', 'w3-total-cache' ),
127
+ 'control' => 'checkbox',
128
+ 'checkbox_label' => 'Flush CloudFlare on Post Modifications',
129
+ 'description' =>
130
+ 'Enable when you have html pages cached on CloudFlare level.'
131
+ )
132
+ );
133
+
134
+ ?>
135
+ </table>
136
+ <?php endif; ?>
137
+
138
+
139
+ <?php Util_Ui::button_config_save( 'extension_cloudflare_general' ); ?>
140
+ <?php Util_Ui::postbox_footer(); ?>
141
+
142
+
143
+ <?php if ( $state == 'available' ): ?>
144
+ <?php Util_Ui::postbox_header( __( 'CloudFlare: Caching', 'w3-total-cache' ), '', 'general' ); ?>
145
+ <table class="form-table">
146
+
147
+ <?php
148
+ self::cloudflare_checkbox( $settings, array(
149
+ 'key' => 'development_mode',
150
+ 'label' => 'Development mode:',
151
+ 'description' => 'Development Mode temporarily allows you to enter development mode for your websites if you need to make changes to your site. This will bypass CloudFlare\'s accelerated cache and slow down your site, but is useful if you are making changes to cacheable content (like images, css, or JavaScript) and would like to see those changes right away.'
152
+ ) );
153
+ self::cloudflare_selectbox( $settings, array(
154
+ 'key' => 'cache_level',
155
+ 'label' => __( 'Cache level:', 'w3-total-cache' ),
156
+ 'values' => array(
157
+ '' => '',
158
+ 'aggressive' => 'Aggressive (cache all static resources, including ones with a query string)',
159
+ 'basic' => 'Basic (cache most static resources (i.e., css, images, and JavaScript)',
160
+ 'simplified' => 'Simplified (ignore the query string when delivering a cached resource)'
161
+ ),
162
+ 'description' => 'How the content is cached by CloudFlare'
163
+ ) );
164
+ self::cloudflare_checkbox( $settings, array(
165
+ 'key' => 'sort_query_string_for_cache',
166
+ 'label' => 'Query String Sorting',
167
+ 'description' => 'CloudFlare will treat files with the same query strings as the same file in cache, regardless of the order of the query strings.',
168
+ ) );
169
+ self::cloudflare_selectbox( $settings, array(
170
+ 'key' => 'browser_cache_ttl',
171
+ 'label' => 'Browser Cache TTL',
172
+ 'values' => array(
173
+ '' => '',
174
+ '30' => '30',
175
+ '60' => '60',
176
+ '300' => '300',
177
+ '1200' => '1200',
178
+ '1800' => '1800',
179
+ '3600' => '3600',
180
+ '7200' => '7200',
181
+ '10800' => '10800',
182
+ '14400' => '14400',
183
+ '18000' => '18000',
184
+ '28800' => '28800',
185
+ '43200' => '43200',
186
+ '57600' => '57600',
187
+ '72000' => '72000',
188
+ '86400' => '86400',
189
+ '172800' => '172800',
190
+ '259200' => '259200',
191
+ '345600' => '345600',
192
+ '432000' => '432000',
193
+ '691200' => '691200',
194
+ '1382400' => '1382400',
195
+ '2073600' => '2073600',
196
+ '2678400' => '2678400',
197
+ '5356800' => '5356800',
198
+ '16070400' => '16070400',
199
+ '31536000' => '31536000'
200
+ ),
201
+ 'description' => 'Browser Cache TTL (in seconds) specifies how long CloudFlare-cached resources will remain on your visitors\' computers.',
202
+ ) );
203
+ self::cloudflare_selectbox( $settings, array(
204
+ 'key' => 'challenge_ttl',
205
+ 'label' => ' Challenge TTL',
206
+ 'values' => array(
207
+ '' => '',
208
+ '300' => '300',
209
+ '900' => '900',
210
+ '1800' => '1800',
211
+ '2700' => '2700',
212
+ '3600' => '3600',
213
+ '7200' => '7200',
214
+ '10800' => '10800',
215
+ '14400' => '14400',
216
+ '28800' => '28800',
217
+ '57600' => '57600',
218
+ '86400' => '86400',
219
+ '604800' => '604800',
220
+ '2592000' => '2592000',
221
+ '31536000' => '31536000'
222
+ ),
223
+ 'description' => 'Specify how long a visitor is allowed access to your site after successfully completing a challenge (such as a CAPTCHA). After the TTL has expired the visitor will have to complete a new challenge.',
224
+ ) );
225
+ self::cloudflare_selectbox( $settings, array(
226
+ 'key' => 'edge_cache_ttl',
227
+ 'label' => 'Edge Cache TTL',
228
+ 'values' => array(
229
+ '' => '',
230
+ '300' => '300',
231
+ '900' => '900',
232
+ '1800' => '1800',
233
+ '2700' => '2700',
234
+ '3600' => '3600',
235
+ '7200' => '7200',
236
+ '10800' => '10800',
237
+ '14400' => '14400',
238
+ '28800' => '28800',
239
+ '57600' => '57600',
240
+ '86400' => '86400',
241
+ '604800' => '604800',
242
+ '2592000' => '2592000',
243
+ '31536000' => '31536000'
244
+ ),
245
+ 'description' => 'Controls how long CloudFlare\'s edge servers will cache a resource before getting back to your server for a fresh copy.',
246
+ ) );
247
+
248
+ echo '</table>';
249
+ self::cloudflare_button_save( 'caching' );
250
+ Util_Ui::postbox_footer();
251
+ Util_Ui::postbox_header( __( 'CloudFlare: Content Processing', 'w3-total-cache' ), '', 'general' );
252
+ echo '<table class="form-table">';
253
+
254
+
255
+ self::cloudflare_selectbox( $settings, array(
256
+ 'key' => 'rocket_loader',
257
+ 'label' => __( 'Rocket Loader:', 'w3-total-cache' ),
258
+ 'values' => array(
259
+ '' => '',
260
+ 'off' => 'Off',
261
+ 'on' => 'On (automatically run on the JavaScript resources on your site)',
262
+ 'manual' => 'Manual (run when attribute present only)'
263
+ ),
264
+ 'description' => 'Rocket Loader is a general-purpose asynchronous JavaScript loader coupled with a lightweight virtual browser which can safely run any JavaScript code after window.onload.'
265
+ ) );
266
+ self::cloudflare_checkbox( $settings, array(
267
+ 'key' => 'minify_js',
268
+ 'label' => 'Minify JS',
269
+ 'description' => 'Minify JavaScript files.',
270
+ ) );
271
+ self::cloudflare_checkbox( $settings, array(
272
+ 'key' => 'minify_css',
273
+ 'label' => 'Minify CSS',
274
+ 'description' => 'Minify CSS files.',
275
+ ) );
276
+ self::cloudflare_checkbox( $settings, array(
277
+ 'key' => 'minify_html',
278
+ 'label' => 'Minify HTML',
279
+ 'description' => 'Minify HTML content.',
280
+ ) );
281
+ self::cloudflare_checkbox( $settings, array(
282
+ 'key' => 'server_side_exclude',
283
+ 'label' => 'Server Side Exclude',
284
+ 'description' => 'If there is sensitive content on your website that you want visible to real visitors, but that you want to hide from suspicious visitors, all you have to do is wrap the content with CloudFlare SSE tags.',
285
+ ) );
286
+ self::cloudflare_checkbox( $settings, array(
287
+ 'key' => 'email_obfuscation',
288
+ 'label' => 'Email obfuscation',
289
+ 'description' => 'Encrypt email adresses on your web page from bots, while keeping them visible to humans. ',
290
+ ) );
291
+ self::cloudflare_checkbox( $settings, array(
292
+ 'key' => 'response_buffering',
293
+ 'label' => 'Response Buffering',
294
+ 'description' => 'CloudFlare may buffer the whole payload to deliver it at once to the client versus allowing it to be delivered in chunks.',
295
+ ) );
296
+ self::cloudflare_checkbox( $settings, array(
297
+ 'key' => 'prefetch_preload',
298
+ 'label' => 'Prefetch Preload',
299
+ 'description' => 'CloudFlare will prefetch any URLs that are included in the response headers.',
300
+ ) );
301
+ self::cloudflare_checkbox( $settings, array(
302
+ 'key' => 'mobile_redirect',
303
+ 'label' => 'Mobile Redirect',
304
+ 'description' => 'Automatically redirect visitors on mobile devices to a mobile-optimized subdomain',
305
+ ) );
306
+ self::cloudflare_checkbox( $settings, array(
307
+ 'key' => 'origin_error_page_pass_thru',
308
+ 'label' => 'Enable Error Pages',
309
+ 'description' => 'CloudFlare will proxy customer error pages on any 502,504 errors on origin server instead of showing a default CloudFlare error page. This does not apply to 522 errors and is limited to Enterprise Zones.',
310
+ ) );
311
+
312
+
313
+ echo '</table>';
314
+ self::cloudflare_button_save( 'content_processing' );
315
+ Util_Ui::postbox_footer();
316
+ Util_Ui::postbox_header( __( 'CloudFlare: Image Processing', 'w3-total-cache' ), '', 'general' );
317
+ echo '<table class="form-table">';
318
+
319
+
320
+ self::cloudflare_checkbox( $settings, array(
321
+ 'key' => 'hotlink_protection',
322
+ 'label' => 'Hotlink Protection',
323
+ 'description' => 'When enabled, the Hotlink Protection option ensures that other sites cannot suck up your bandwidth by building pages that use images hosted on your site.',
324
+ ) );
325
+ self::cloudflare_checkbox( $settings, array(
326
+ 'key' => 'mirage',
327
+ 'label' => 'Mirage',
328
+ 'description' => 'Automatically optimize image loading for website visitors on mobile devices',
329
+ ) );
330
+ self::cloudflare_selectbox( $settings, array(
331
+ 'key' => 'polish',
332
+ 'label' => __( 'Images polishing:', 'w3-total-cache' ),
333
+ 'values' => array(
334
+ '' => '',
335
+ 'off' => 'Off',
336
+ 'lossless' => 'Lossless (Reduce the size of PNG, JPEG, and GIF files - no impact on visual quality)',
337
+ 'lossy' => 'Lossy (Further reduce the size of JPEG files for faster image loading)'
338
+ ),
339
+ 'description' => 'Strips metadata and compresses your images for faster page load times.'
340
+ ) );
341
+
342
+
343
+ echo '</table>';
344
+ self::cloudflare_button_save( 'image_processing' );
345
+ Util_Ui::postbox_footer();
346
+ Util_Ui::postbox_header( __( 'CloudFlare: Protection', 'w3-total-cache' ), '', 'general' );
347
+ echo '<table class="form-table">';
348
+
349
+
350
+ self::cloudflare_selectbox( $settings, array(
351
+ 'key' => 'security_level',
352
+ 'label' => __( 'Security Level:', 'w3-total-cache' ),
353
+ 'values' => array(
354
+ '' => '',
355
+ 'essentially_off' => 'Off',
356
+ 'low' => 'Low',
357
+ 'medium' => 'Medium',
358
+ 'high' => 'High',
359
+ 'under_attack' => 'Under Attack'
360
+ ),
361
+ 'description' => 'security profile for your website, which will automatically adjust each of the security settings.'
362
+ ) );
363
+ self::cloudflare_checkbox( $settings, array(
364
+ 'key' => 'browser_check',
365
+ 'label' => 'Browser Integrity Check',
366
+ 'description' => 'Browser Integrity Check is similar to Bad Behavior and looks for common HTTP headers abused most commonly by spammers and denies access to your page. It will also challenge visitors that do not have a user agent or a non standard user agent (also commonly used by abuse bots, crawlers or visitors).',
367
+ ) );
368
+ self::cloudflare_checkbox( $settings, array(
369
+ 'key' => 'always_online',
370
+ 'label' => 'Always Online',
371
+ 'description' => 'When enabled, Always Online will serve pages from our cache if your server is offline',
372
+ ) );
373
+ self::cloudflare_checkbox( $settings, array(
374
+ 'key' => 'waf',
375
+ 'label' => 'Web Application Firewall',
376
+ 'description' => 'The WAF examines HTTP requests to your website. It inspects both GET and POST requests and applies rules to help filter out illegitimate traffic from legitimate website visitors.'
377
+ ) );
378
+ self::cloudflare_checkbox( $settings, array(
379
+ 'key' => 'advanced_ddos',
380
+ 'label' => 'Advanced DDOS Protection',
381
+ 'description' => 'Advanced protection from Distributed Denial of Service (DDoS) attacks on your website.',
382
+ ) );
383
+ self::cloudflare_textbox( $settings, array(
384
+ 'key' => 'max_upload',
385
+ 'label' => 'Max Upload',
386
+ 'description' => 'Max size of file allowed for uploading',
387
+ ) );
388
+
389
+
390
+ echo '</table>';
391
+ self::cloudflare_button_save( 'protection' );
392
+ Util_Ui::postbox_footer();
393
+ Util_Ui::postbox_header( __( 'CloudFlare: IP', 'w3-total-cache' ), '', 'general' );
394
+ echo '<table class="form-table">';
395
+
396
+
397
+ self::cloudflare_checkbox( $settings, array(
398
+ 'key' => 'ip_geolocation',
399
+ 'label' => 'IP Geolocation',
400
+ 'description' => 'Enable IP Geolocation to have CloudFlare geolocate visitors to your website and pass the country code to you.',
401
+ ) );
402
+ self::cloudflare_checkbox( $settings, array(
403
+ 'key' => 'ipv6',
404
+ 'label' => 'IPv6',
405
+ 'description' => 'Enable IPv6.',
406
+ ) );
407
+ self::cloudflare_checkbox( $settings, array(
408
+ 'key' => 'pseudo_ipv4',
409
+ 'label' => 'Pseudo IPv4',
410
+ 'description' => 'Support for IPv6 addresses in legacy IPv4 applications.',
411
+ ) );
412
+ self::cloudflare_checkbox( $settings, array(
413
+ 'key' => 'true_client_ip_header',
414
+ 'label' => 'True Client IP',
415
+ 'description' => 'Allows customer to continue to use True Client IP (Akamai feature) in the headers we send to the origin.',
416
+ ) );
417
+
418
+
419
+ echo '</table>';
420
+ self::cloudflare_button_save( 'ip' );
421
+ Util_Ui::postbox_footer();
422
+ Util_Ui::postbox_header( __( 'CloudFlare: SSL', 'w3-total-cache' ), '', 'general' );
423
+ echo '<table class="form-table">';
424
+
425
+
426
+ self::cloudflare_selectbox( $settings, array(
427
+ 'key' => 'ssl',
428
+ 'label' => 'SSL',
429
+ 'values' => array(
430
+ '' => '',
431
+ 'off' => 'Off',
432
+ 'flexible' => 'Flexible (https to end-user only)',
433
+ 'full' => 'Full (https everywhere)',
434
+ 'full_strict' => 'Strict'
435
+ ),
436
+ 'description' => 'SSL encrypts your visitor\'s connection and safeguards credit card numbers and other personal data to and from your website.',
437
+ ) );
438
+ self::cloudflare_checkbox( $settings, array(
439
+ 'key' => 'security_header',
440
+ 'label' => 'Security Header (HSTS)',
441
+ 'description' => 'Enables or disables HSTS header.',
442
+ ) );
443
+ self::cloudflare_checkbox( $settings, array(
444
+ 'key' => 'tls_1_2_only',
445
+ 'label' => 'TLS 1.2 Only',
446
+ 'description' => 'Enable Crypto TLS 1.2 feature for this zone and prevent use of previous versions.',
447
+ ) );
448
+ self::cloudflare_checkbox( $settings, array(
449
+ 'key' => 'tls_client_auth',
450
+ 'label' => 'TLS Client Auth',
451
+ 'description' => 'TLS Client Auth requires CloudFlare to connect to your origin server using a client certificate',
452
+ ) );
453
+
454
+ echo '</table>';
455
+ self::cloudflare_button_save( 'ssl' );
456
+ Util_Ui::postbox_footer();
457
+ endif;
458
+ ?>
459
+ </div>
460
+ </form>
Extension_CloudFlare_Plugin.php ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 ObjectCache plugin
6
+ */
7
+ define( 'W3TC_MARKER_BEGIN_CLOUDFLARE', '# BEGIN W3TC CloudFlare' );
8
+ define( 'W3TC_MARKER_END_CLOUDFLARE', '# END W3TC CloudFlare' );
9
+
10
+ /**
11
+ * Class W3_Plugin_CloudFlare
12
+ */
13
+ class Extension_CloudFlare_Plugin {
14
+ private $_config;
15
+ private $flush_operation_requested = false;
16
+
17
+
18
+
19
+ function __construct() {
20
+ $this->_config = Dispatcher::config();
21
+ }
22
+
23
+
24
+
25
+ /**
26
+ * Runs plugin
27
+ */
28
+ function run() {
29
+ add_filter( 'w3tc_config_default_values', array(
30
+ $this, 'w3tc_config_default_values' ) );
31
+
32
+ add_action( 'wp_set_comment_status',
33
+ array( $this, 'set_comment_status' ), 1, 2 );
34
+
35
+ // priority is important, see do_action call of that action
36
+ add_action( 'w3tc_flush_all', array( $this, 'w3tc_flush_all' ), 3000, 1 );
37
+
38
+ add_filter( 'w3tc_flush_execute_delayed_operations',
39
+ array( $this, 'w3tc_flush_execute_delayed_operations' ), 3000, 1 );
40
+
41
+ $this->fix_remote_addr();
42
+
43
+ // if page caching is enabled on CF - attach to post modifications
44
+ // and flush it
45
+ if ( $this->_config->get_boolean( array( 'cloudflare', 'pagecache' ) ) ) {
46
+ Util_AttachToActions::flush_posts_on_actions();
47
+
48
+ add_action( 'w3tc_flush_post',
49
+ array( $this, 'w3tc_flush_xxx' ), 3000 );
50
+ add_action( 'w3tc_flushable_posts', '__return_true', 3000 );
51
+ add_action( 'w3tc_flush_posts',
52
+ array( $this, 'w3tc_flush_xxx' ), 3000 );
53
+ }
54
+ }
55
+
56
+
57
+
58
+ public function w3tc_config_default_values( $default_values ) {
59
+ $default_values['cloudflare'] = array(
60
+ 'widget_interval' => 30,
61
+ 'widget_cache_mins' => 5,
62
+ 'timelimit.api_request' => 180
63
+ );
64
+
65
+ return $default_values;
66
+ }
67
+
68
+
69
+
70
+ public function w3tc_flush_all( $extras ) {
71
+ if ( is_array( $extras ) && isset( $extras['cloudflare'] ) &&
72
+ $extras['cloudflare'] == 'skip' )
73
+ return;
74
+
75
+ $this->flush_operation_requested = true;
76
+ }
77
+
78
+
79
+
80
+ public function w3tc_flush_xxx() {
81
+ $this->flush_operation_requested = true;
82
+ }
83
+
84
+
85
+
86
+ public function w3tc_flush_execute_delayed_operations( $actions_made ) {
87
+ if ( $this->flush_operation_requested ) {
88
+ $c = Dispatcher::config();
89
+ $api = new Extension_CloudFlare_Api( array(
90
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
91
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) ),
92
+ 'zone_id' => $c->get_string( array( 'cloudflare', 'zone_id' ) ),
93
+ 'timelimit_api_request' => $c->get_integer(
94
+ array( 'cloudflare', 'timelimit.api_request' ) )
95
+ )
96
+ );
97
+
98
+ $action_made = array(
99
+ 'module' => 'cloudflare'
100
+ );
101
+
102
+ try {
103
+ $api->purge();
104
+ } catch ( \Exception $ex ) {
105
+ $action_made['error'] =
106
+ 'Failed to purge CloudFlare cache: ' . $ex->getMessage();
107
+ }
108
+
109
+ $this->flush_operation_requested = false;
110
+
111
+ $actions_made[] = $action_made;
112
+ }
113
+
114
+ return $actions_made;
115
+ }
116
+
117
+
118
+
119
+ function set_comment_status( $id, $status ) {
120
+ if ( $status == "spam" ) {
121
+ $comment = get_comment( $id );
122
+ $value = array(
123
+ 'a' => $comment->comment_author,
124
+ 'am' => $comment->comment_author_email,
125
+ 'ip' => $comment->comment_author_IP,
126
+ 'con' => substr( $comment->comment_content, 0, 100 ) );
127
+
128
+ $c = Dispatcher::config();
129
+ $api = new Extension_CloudFlare_Api( array(
130
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
131
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) ),
132
+ 'zone_id' => $c->get_string( array( 'cloudflare', 'zone_id' ) ),
133
+ 'timelimit_api_request' => $c->get_integer(
134
+ array( 'cloudflare', 'timelimit.api_request' ) )
135
+ )
136
+ );
137
+ $api->external_event( 'WP_SPAM', json_encode( $value ) );
138
+ }
139
+ }
140
+
141
+
142
+
143
+ public function menu_bar( $menu_items ) {
144
+ $menu_items = array_merge( $menu_items, array(
145
+ array(
146
+ 'id' => 'cloudflare',
147
+ 'title' => __( 'CloudFlare', 'w3-total-cache' ),
148
+ 'href' => 'https://www.cloudflare.com'
149
+ ),
150
+ array(
151
+ 'id' => 'cloudflare-my-websites',
152
+ 'parent' => 'cloudflare',
153
+ 'title' => __( 'My Websites', 'w3-total-cache' ),
154
+ 'href' => 'https://www.cloudflare.com/my-websites.html'
155
+ ),
156
+ array(
157
+ 'id' => 'cloudflare-analytics',
158
+ 'parent' => 'cloudflare',
159
+ 'title' => __( 'Analytics', 'w3-total-cache' ),
160
+ 'href' => 'https://www.cloudflare.com/analytics.html'
161
+ ),
162
+ array(
163
+ 'id' => 'cloudflare-account',
164
+ 'parent' => 'cloudflare',
165
+ 'title' => __( 'Account', 'w3-total-cache' ),
166
+ 'href' => 'https://www.cloudflare.com/my-account.html'
167
+ )
168
+ ) );
169
+ return $menu_items;
170
+ }
171
+
172
+
173
+ /**
174
+ * Fix client's IP-address
175
+ */
176
+ private function fix_remote_addr() {
177
+ if ( empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) )
178
+ return;
179
+
180
+ if ( strpos( $_SERVER["REMOTE_ADDR"], ":" ) === FALSE ) {
181
+ $ip4_ranges = $this->_config->get_array( array(
182
+ 'cloudflare', 'ips.ip4' ) );
183
+ foreach ( $ip4_ranges as $range ) {
184
+ if ( $this->ipv4_in_range( $_SERVER['REMOTE_ADDR'], $range ) ) {
185
+ $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
186
+ break;
187
+ }
188
+ }
189
+ } else {
190
+ $ip6_ranges = $this->_config->get_array( array(
191
+ 'cloudflare', 'ips.ip6' ) );
192
+ $ip6 = $this->get_ipv6_full( $_SERVER["REMOTE_ADDR"] );
193
+ foreach ( $ip6_ranges as $range ) {
194
+ if ( $this->ipv6_in_range( $ip6, $range ) ) {
195
+ $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
196
+ break;
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+
203
+
204
+ /*
205
+ * ip_in_range.php - Function to determine if an IP is located in a
206
+ * specific range as specified via several alternative
207
+ * formats.
208
+ *
209
+ * Network ranges can be specified as:
210
+ * 1. Wildcard format: 1.2.3.*
211
+ * 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0
212
+ * 3. Start-End IP format: 1.2.3.0-1.2.3.255
213
+ *
214
+ * Return value BOOLEAN : ip_in_range($ip, $range);
215
+ *
216
+ * Copyright 2008: Paul Gregg <pgregg@pgregg.com>
217
+ * 10 January 2008
218
+ * Version: 1.2
219
+ *
220
+ * Source website: http://www.pgregg.com/projects/php/ip_in_range/
221
+ * Version 1.2
222
+ *
223
+ * This software is Donationware - if you feel you have benefited from
224
+ * the use of this tool then please consider a donation. The value of
225
+ * which is entirely left up to your discretion.
226
+ * http://www.pgregg.com/donate/
227
+ *
228
+ * Please do not remove this header, or source attibution from this file.
229
+ * Modified by James Greene <james@cloudflare.com> to include IPV6 support
230
+ * (original version only supported IPV4).
231
+ * 21 May 2012
232
+ */
233
+ /**
234
+ * ipv4_in_range
235
+ * This function takes 2 arguments, an IP address and a "range" in several
236
+ * different formats.
237
+ * Network ranges can be specified as:
238
+ * 1. Wildcard format: 1.2.3.*
239
+ * 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0
240
+ * 3. Start-End IP format: 1.2.3.0-1.2.3.255
241
+ * The function will return true if the supplied IP is within the range.
242
+ * Note little validation is done on the range inputs - it expects you to
243
+ * use one of the above 3 formats.
244
+ */
245
+ private function ipv4_in_range( $ip, $range ) {
246
+ if ( strpos( $range, '/' ) !== false ) {
247
+ // $range is in IP/NETMASK format
248
+ list( $range, $netmask ) = explode( '/', $range, 2 );
249
+ if ( strpos( $netmask, '.' ) !== false ) {
250
+ // $netmask is a 255.255.0.0 format
251
+ $netmask = str_replace( '*', '0', $netmask );
252
+ $netmask_dec = ip2long( $netmask );
253
+ return ( ip2long( $ip ) & $netmask_dec ) == ( ip2long( $range ) & $netmask_dec );
254
+ } else {
255
+ // $netmask is a CIDR size block
256
+ // fix the range argument
257
+ $x = explode( '.', $range );
258
+ while ( count( $x )<4 ) $x[] = '0';
259
+ list( $a, $b, $c, $d ) = $x;
260
+ $range = sprintf( "%u.%u.%u.%u", empty( $a )?'0':$a, empty( $b )?'0':$b, empty( $c )?'0':$c, empty( $d )?'0':$d );
261
+ $range_dec = ip2long( $range );
262
+ $ip_dec = ip2long( $ip );
263
+
264
+ // Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
265
+ //$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
266
+
267
+ // Strategy 2 - Use math to create it
268
+ $wildcard_dec = pow( 2, ( 32-$netmask ) ) - 1;
269
+ $netmask_dec = ~ $wildcard_dec;
270
+
271
+ return ( $ip_dec & $netmask_dec ) == ( $range_dec & $netmask_dec );
272
+ }
273
+ } else {
274
+ // range might be 255.255.*.* or 1.2.3.0-1.2.3.255
275
+ if ( strpos( $range, '*' ) !==false ) { // a.b.*.* format
276
+ // Just convert to A-B format by setting * to 0 for A and 255 for B
277
+ $lower = str_replace( '*', '0', $range );
278
+ $upper = str_replace( '*', '255', $range );
279
+ $range = "$lower-$upper";
280
+ }
281
+
282
+ if ( strpos( $range, '-' )!==false ) { // A-B format
283
+ list( $lower, $upper ) = explode( '-', $range, 2 );
284
+ $lower_dec = (float)sprintf( "%u", ip2long( $lower ) );
285
+ $upper_dec = (float)sprintf( "%u", ip2long( $upper ) );
286
+ $ip_dec = (float)sprintf( "%u", ip2long( $ip ) );
287
+ return ( $ip_dec>=$lower_dec ) && ( $ip_dec<=$upper_dec );
288
+ }
289
+ return false;
290
+ }
291
+ }
292
+
293
+
294
+
295
+ private function ip2long6( $ip ) {
296
+ if ( substr_count( $ip, '::' ) ) {
297
+ $ip = str_replace( '::', str_repeat( ':0000', 8 - substr_count( $ip, ':' ) ) . ':', $ip );
298
+ }
299
+
300
+ $ip = explode( ':', $ip );
301
+ $r_ip = '';
302
+ foreach ( $ip as $v ) {
303
+ $r_ip .= str_pad( base_convert( $v, 16, 2 ), 16, 0, STR_PAD_LEFT );
304
+ }
305
+
306
+ return base_convert( $r_ip, 2, 10 );
307
+ }
308
+
309
+
310
+
311
+ /**
312
+ * Get the ipv6 full format and return it as a decimal value.
313
+ */
314
+ private function get_ipv6_full( $ip ) {
315
+ $pieces = explode( "/", $ip, 2 );
316
+ $left_piece = $pieces[0];
317
+ $right_piece = $pieces[1];
318
+
319
+ // Extract out the main IP pieces
320
+ $ip_pieces = explode( "::", $left_piece, 2 );
321
+ $main_ip_piece = $ip_pieces[0];
322
+ $last_ip_piece = $ip_pieces[1];
323
+
324
+ // Pad out the shorthand entries.
325
+ $main_ip_pieces = explode( ":", $main_ip_piece );
326
+ foreach ( $main_ip_pieces as $key=>$val ) {
327
+ $main_ip_pieces[$key] = str_pad( $main_ip_pieces[$key], 4, "0", STR_PAD_LEFT );
328
+ }
329
+
330
+ // Check to see if the last IP block (part after ::) is set
331
+ $last_piece = "";
332
+ $size = count( $main_ip_pieces );
333
+ if ( trim( $last_ip_piece ) != "" ) {
334
+ $last_piece = str_pad( $last_ip_piece, 4, "0", STR_PAD_LEFT );
335
+
336
+ // Build the full form of the IPV6 address considering the last IP block set
337
+ for ( $i = $size; $i < 7; $i++ ) {
338
+ $main_ip_pieces[$i] = "0000";
339
+ }
340
+ $main_ip_pieces[7] = $last_piece;
341
+ }
342
+ else {
343
+ // Build the full form of the IPV6 address
344
+ for ( $i = $size; $i < 8; $i++ ) {
345
+ $main_ip_pieces[$i] = "0000";
346
+ }
347
+ }
348
+
349
+ // Rebuild the final long form IPV6 address
350
+ $final_ip = implode( ":", $main_ip_pieces );
351
+
352
+ return $this->ip2long6( $final_ip );
353
+ }
354
+
355
+
356
+
357
+ /**
358
+ * Determine whether the IPV6 address is within range.
359
+ * $ip is the IPV6 address in decimal format to check if its within the IP range created by the cloudflare IPV6 address, $range_ip.
360
+ * $ip and $range_ip are converted to full IPV6 format.
361
+ * Returns true if the IPV6 address, $ip, is within the range from $range_ip. False otherwise.
362
+ */
363
+ private function ipv6_in_range( $ip, $range_ip ) {
364
+ $pieces = explode( "/", $range_ip, 2 );
365
+ $left_piece = $pieces[0];
366
+ $right_piece = $pieces[1];
367
+
368
+ // Extract out the main IP pieces
369
+ $ip_pieces = explode( "::", $left_piece, 2 );
370
+ $main_ip_piece = $ip_pieces[0];
371
+ $last_ip_piece = $ip_pieces[1];
372
+
373
+ // Pad out the shorthand entries.
374
+ $main_ip_pieces = explode( ":", $main_ip_piece );
375
+ foreach ( $main_ip_pieces as $key=>$val ) {
376
+ $main_ip_pieces[$key] = str_pad( $main_ip_pieces[$key], 4, "0", STR_PAD_LEFT );
377
+ }
378
+
379
+ // Create the first and last pieces that will denote the IPV6 range.
380
+ $first = $main_ip_pieces;
381
+ $last = $main_ip_pieces;
382
+
383
+ // Check to see if the last IP block (part after ::) is set
384
+ $last_piece = "";
385
+ $size = count( $main_ip_pieces );
386
+ if ( trim( $last_ip_piece ) != "" ) {
387
+ $last_piece = str_pad( $last_ip_piece, 4, "0", STR_PAD_LEFT );
388
+
389
+ // Build the full form of the IPV6 address considering the last IP block set
390
+ for ( $i = $size; $i < 7; $i++ ) {
391
+ $first[$i] = "0000";
392
+ $last[$i] = "ffff";
393
+ }
394
+ $main_ip_pieces[7] = $last_piece;
395
+ }
396
+ else {
397
+ // Build the full form of the IPV6 address
398
+ for ( $i = $size; $i < 8; $i++ ) {
399
+ $first[$i] = "0000";
400
+ $last[$i] = "ffff";
401
+ }
402
+ }
403
+
404
+ // Rebuild the final long form IPV6 address
405
+ $first = $this->ip2long6( implode( ":", $first ) );
406
+ $last = $this->ip2long6( implode( ":", $last ) );
407
+ $in_range = ( $ip >= $first && $ip <= $last );
408
+
409
+ return $in_range;
410
+ }
411
+
412
+ }
413
+
414
+
415
+
416
+ $p = new Extension_CloudFlare_Plugin();
417
+ $p->run();
418
+
419
+ if ( is_admin() ) {
420
+ $p = new Extension_CloudFlare_Plugin_Admin();
421
+ $p->run();
422
+ }
Extension_CloudFlare_Plugin_Admin.php ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_CloudFlare_Plugin_Admin {
5
+ private $_config;
6
+ private $api;
7
+
8
+
9
+
10
+ static public function w3tc_extensions( $extensions, $config ) {
11
+ global $current_user;
12
+ $message = array();
13
+ $message[] = 'CloudFlare';
14
+ $cloudflare_signup_email = '';
15
+ $cloudflare_signup_user = '';
16
+
17
+ if ( is_a( $current_user, 'WP_User' ) ) {
18
+ if ( $current_user->user_email ) {
19
+ $cloudflare_signup_email = $current_user->user_email;
20
+ }
21
+
22
+ if ( $current_user->user_login && $current_user->user_login != 'admin' ) {
23
+ $cloudflare_signup_user = $current_user->user_login;
24
+ }
25
+ }
26
+ $extensions['cloudflare'] = array (
27
+ 'name' => 'CloudFlare',
28
+ 'author' => 'W3 EDGE',
29
+ 'description' => sprintf( __( 'CloudFlare protects and accelerates websites. <a href="%s" target="_blank">Sign up now for free</a> to get started,
30
+ or if you have an account simply log in to obtain your <abbr title="Application Programming Interface">API</abbr> key from the <a target="_blank" href="https://www.cloudflare.com/my-account">account page</a> to enter it on the General Settings box that appears after plugin activation.
31
+ Contact the CloudFlare <a href="http://www.cloudflare.com/help.html" target="_blank">support team</a> with any questions.', 'w3-total-cache' ), 'https://www.cloudflare.com/sign-up.html?affiliate=w3edge&amp;seed_domain=' . Util_Environment::host() . '&amp;email=' . htmlspecialchars( $cloudflare_signup_email ) . '&amp;username=' . htmlspecialchars( $cloudflare_signup_user ) ),
32
+ 'author_uri' => 'https://www.w3-edge.com/',
33
+ 'extension_uri' => 'https://www.w3-edge.com/',
34
+ 'extension_id' => 'cloudflare',
35
+ 'version' => '0.3',
36
+ 'enabled' => true,
37
+ 'requirements' => implode( ', ', $message ),
38
+ 'path' => 'w3-total-cache/Extension_CloudFlare_Plugin.php'
39
+ );
40
+
41
+ return $extensions;
42
+ }
43
+
44
+
45
+
46
+ function run() {
47
+ $c = Dispatcher::config();
48
+ $this->api = new Extension_CloudFlare_Api( array(
49
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
50
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) ),
51
+ 'zone_id' => $c->get_string( array( 'cloudflare', 'zone_id' ) ),
52
+ 'timelimit_api_request' => $c->get_integer(
53
+ array( 'cloudflare', 'timelimit.api_request' ) )
54
+ )
55
+ );
56
+ $this->_config = $c;
57
+
58
+ add_filter( 'w3tc_dashboard_actions',
59
+ array( $this, 'w3tc_dashboard_actions' ) );
60
+
61
+ $widget = new Extension_CloudFlare_Widget();
62
+ $widget->init();
63
+
64
+ // modify settings page
65
+ add_filter( 'w3tc_settings_general_anchors',
66
+ array( $this, 'w3tc_settings_general_anchors' ) );
67
+ add_action( 'w3tc_settings_general_boxarea_cloudflare',
68
+ array( $this, 'w3tc_settings_general_boxarea_cloudflare' ) );
69
+
70
+ add_action( 'wp_ajax_w3tc_cloudflare_api_request',
71
+ array( $this, 'action_cloudflare_api_request' ) );
72
+
73
+ // modify main menu
74
+ add_action( 'w3tc_extension_page_cloudflare', array(
75
+ '\W3TC\Extension_CloudFlare_Page',
76
+ 'w3tc_extension_page_cloudflare'
77
+ ) );
78
+ add_filter( 'w3tc_admin_bar_menu', array( $this, 'w3tc_admin_bar_menu' ) );
79
+
80
+ // dashboard
81
+ add_action( 'admin_print_scripts-toplevel_page_w3tc_dashboard',
82
+ array( $this, 'admin_print_scripts_w3tc_dashboard' ) );
83
+ add_filter( 'w3tc_admin_actions', array( $this, 'w3tc_admin_actions' ) );
84
+
85
+ // own settings page
86
+ add_action( 'admin_print_scripts-performance_page_w3tc_extensions',
87
+ array( '\W3TC\Extension_CloudFlare_Page',
88
+ 'admin_print_scripts_w3tc_extensions'
89
+ ) );
90
+ add_action( 'w3tc_ajax',
91
+ array( '\W3TC\Extension_CloudFlare_Popup', 'w3tc_ajax' ) );
92
+
93
+ // add check to comments page
94
+ add_filter( 'comment_row_actions', array( $this, 'comment_row_actions' ),
95
+ 10, 2 );
96
+ add_action( 'admin_print_styles-edit-comments.php',
97
+ array( $this, 'admin_print_styles_edit_comments' ) );
98
+ add_action( 'admin_print_scripts-edit-comments.php',
99
+ array( $this, 'admin_print_scripts_edit_comments' ) );
100
+ add_action( 'wp_ajax_w3tc_cloudflare_ip_check',
101
+ array( $this, 'w3tc_cloudflare_ip_check' ) );
102
+
103
+ // add notices about api health
104
+ if ( Util_Admin::is_w3tc_admin_page() ) {
105
+ add_action( 'admin_notices', array(
106
+ $this,
107
+ 'admin_notices'
108
+ ) );
109
+ add_action( 'network_admin_notices', array(
110
+ $this,
111
+ 'admin_notices'
112
+ ) );
113
+ }
114
+
115
+ $this->check_ip_versions();
116
+ }
117
+
118
+
119
+
120
+ function admin_notices() {
121
+ $plugins = get_plugins();
122
+ if ( array_key_exists( 'cloudflare/cloudflare.php', $plugins ) && $this->_config->get_boolean( 'notes.cloudflare_plugin' ) ) {
123
+
124
+ echo sprintf( '<div class="error"><p>%s %s</p></div>', __( 'CloudFlare plugin detected. We recommend removing the
125
+ plugin as it offers no additional capabilities when W3 Total Cache is installed. This message will disappear
126
+ when CloudFlare is removed.', 'w3-total-cache' ),
127
+ Util_Ui::button_hide_note( 'Hide this message', 'cloudflare_plugin' )
128
+ );
129
+ }
130
+ }
131
+
132
+
133
+
134
+ public function w3tc_admin_bar_menu( $menu_items ) {
135
+ $menu_items['20810.cloudflare'] = array(
136
+ 'id' => 'w3tc_flush_cloudflare',
137
+ 'parent' => 'w3tc_flush',
138
+ 'title' => __( 'CloudFlare: All', 'w3-total-cache' ),
139
+ 'href' => wp_nonce_url( network_admin_url(
140
+ 'admin.php?page=w3tc_dashboard&amp;w3tc_cloudflare_flush' ), 'w3tc' )
141
+ );
142
+
143
+ return $menu_items;
144
+ }
145
+
146
+
147
+
148
+ /**
149
+ * Check if last check has expired. If so update CloudFlare ips
150
+ */
151
+ function check_ip_versions() {
152
+ $state = Dispatcher::config_state_master();
153
+
154
+ if ( $state->get_integer( 'extension.cloudflare.next_ips_check' ) < time() ) {
155
+ // update asap to avoid multiple processes entering the check
156
+ $state->set( 'extension.cloudflare.next_ips_check',
157
+ time() + 7 * 24 * 60 * 60 );
158
+
159
+ $data = array();
160
+ try {
161
+ $data = $this->api->get_ip_ranges();
162
+ } catch ( \Exception $ex ) {
163
+ }
164
+
165
+ if ( isset( $data['ip4'] ) ) {
166
+ $state->set( 'extension.cloudflare.ips.ip4', $data['ip4'] );
167
+ }
168
+ if ( isset( $data['ip6'] ) ) {
169
+ $state->set( 'extension.cloudflare.ips.ip6', $data['ip6'] );
170
+ }
171
+
172
+ $state->save();
173
+ }
174
+ }
175
+
176
+
177
+
178
+ /**
179
+ * Send CloudFlare API request
180
+ *
181
+ * @return void
182
+ */
183
+ function action_cloudflare_api_request() {
184
+ $result = false;
185
+ $response = null;
186
+
187
+ $actions = array(
188
+ 'dev_mode',
189
+ 'sec_lvl',
190
+ 'fpurge_ts'
191
+ );
192
+
193
+ $email = Util_Request::get_string( 'email' );
194
+ $key = Util_Request::get_string( 'key' );
195
+ $zone = Util_Request::get_string( 'zone' );
196
+ $action = Util_Request::get_string( 'command' );
197
+ $value = Util_Request::get_string( 'value' );
198
+ $nonce = Util_Request::get_string( '_wpnonce' );
199
+
200
+ if ( !wp_verify_nonce( $nonce, 'w3tc' ) ) {
201
+ $error ='Access denied.';
202
+ } elseif ( !$email ) {
203
+ $error = 'Empty email.';
204
+ } elseif ( !filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
205
+ $error = 'Invalid email.';
206
+ } elseif ( !$key ) {
207
+ $error = 'Empty key.';
208
+ } elseif ( !$zone ) {
209
+ $error = 'Empty zone.';
210
+ } elseif ( strpos( $zone, '.' ) === false ) {
211
+ $error = 'Invalid domain.';
212
+ } elseif ( !in_array( $action, $actions ) ) {
213
+ $error = 'Invalid action.';
214
+ } else {
215
+ $config = array(
216
+ 'email' => $email,
217
+ 'key' => $key,
218
+ 'zone' => $zone
219
+ );
220
+
221
+ @$this->api = new Extension_CloudFlare_Api( $config );
222
+
223
+ @set_time_limit( $this->_config->get_integer( array( 'cloudflare', 'timelimit.api_request' ) ) );
224
+ $response = $this->api->api_request( $action, $value );
225
+
226
+ if ( $response ) {
227
+ if ( $response->result == 'success' ) {
228
+ $result = true;
229
+ $error = 'OK';
230
+ } else {
231
+ $error = $response->msg;
232
+ }
233
+ } else {
234
+ $error = 'Unable to make CloudFlare API request.';
235
+ }
236
+ }
237
+
238
+ $return = array(
239
+ 'result' => $result,
240
+ 'error' => $error,
241
+ 'response' => $response
242
+ );
243
+
244
+ echo json_encode( $return );
245
+ exit();
246
+ }
247
+
248
+
249
+
250
+ /**
251
+ * W3TC Dashboard page modifications
252
+ */
253
+ public function w3tc_admin_actions( $handlers ) {
254
+ $handlers['cloudflare'] = 'Extension_CloudFlare_AdminActions';
255
+ return $handlers;
256
+ }
257
+
258
+
259
+
260
+ public function admin_print_scripts_w3tc_dashboard() {
261
+ wp_enqueue_script( 'w3tc_extension_cloudflare_dashboard',
262
+ plugins_url( 'Extension_CloudFlare_View_Dashboard.js', W3TC_FILE ),
263
+ array( 'jquery' ), '1.0' );
264
+ }
265
+
266
+
267
+
268
+ public function w3tc_dashboard_actions( $actions ) {
269
+ $email = $this->_config->get_string( array( 'cloudflare', 'email' ) );
270
+ $key = $this->_config->get_string( array( 'cloudflare', 'key' ) );
271
+
272
+ if ( empty( $email ) || empty( $key ) )
273
+ return $actions;
274
+
275
+ $modules = Dispatcher::component( 'ModuleStatus' );
276
+ $can_empty_memcache = $modules->can_empty_memcache();
277
+ $can_empty_opcode = $modules->can_empty_opcode();
278
+ $can_empty_file = $modules->can_empty_file();
279
+ $can_empty_varnish = $modules->can_empty_varnish();
280
+
281
+ $actions[] = ' or <input id="flush_all_except_cf" class="button" type="submit" name="w3tc_cloudflare_flush_all_except_cf" value="'.
282
+ __( 'empty all caches except CloudFlare', 'w3-total-cache' ).'"'.
283
+ ( ( ! $can_empty_memcache && ! $can_empty_opcode && ! $can_empty_file && ! $can_empty_varnish ) ?
284
+ 'disabled="disabled"':'' ) . '> ' . __( 'at once', 'w3-total-cache' );
285
+ return $actions;
286
+ }
287
+
288
+
289
+
290
+ public function w3tc_settings_general_anchors( $anchors ) {
291
+ $anchors[] = array( 'id' => 'cloudflare', 'text' => 'CloudFlare' );
292
+ return $anchors;
293
+ }
294
+
295
+
296
+
297
+ public function w3tc_settings_general_boxarea_cloudflare() {
298
+ $config = $this->_config;
299
+ include W3TC_DIR . '/Extension_CloudFlare_GeneralPage_View.php';
300
+ }
301
+
302
+
303
+
304
+ /**
305
+ * Comments page modification
306
+ */
307
+ public function comment_row_actions( $actions, $comment_id ) {
308
+ $ip = get_comment_author_IP( $comment_id );
309
+ if ( !empty( $ip ) )
310
+ $actions[] = '<a href="#ip=' . urlencode( $ip ) .
311
+ '" class="w3tc_cloudflare_ip_check">CloudFlare IP score</a>';
312
+ return $actions;
313
+ }
314
+
315
+
316
+
317
+ public function admin_print_styles_edit_comments() {
318
+ wp_enqueue_style( 'w3tc_extension_cloudflare_general',
319
+ plugins_url( 'Extension_CloudFlare_View_Comments.css', W3TC_FILE ) );
320
+ }
321
+
322
+
323
+
324
+ public function admin_print_scripts_edit_comments() {
325
+ wp_enqueue_script( 'w3tc_extension_cloudflare_general',
326
+ plugins_url( 'Extension_CloudFlare_View_Comments.js', W3TC_FILE ),
327
+ array( 'jquery' ), '1.0' );
328
+ }
329
+
330
+
331
+
332
+ public function w3tc_cloudflare_ip_check() {
333
+ $api = new Extension_CloudFlare_Api();
334
+
335
+ $ip = $_REQUEST['ip'];
336
+ $response = $this->api->ip_lkup( $ip );
337
+
338
+ $error = true;
339
+
340
+ //var_dump($response);
341
+ if ( !isset( $response->result ) )
342
+ $message = 'API failed';
343
+ else if ( $response->result != 'success' )
344
+ $message = 'API error: ' . $response->msg;
345
+ else if ( !isset( $response->response ) || !isset( $response->response->$ip ) )
346
+ $message = 'no information';
347
+ else if ( !$response->response->$ip ) {
348
+ $message = 'valid IP';
349
+ $error = false;
350
+ } else
351
+ $message = 'invalid IP';
352
+
353
+ echo json_encode( array(
354
+ 'message' => $message,
355
+ 'error' => $error
356
+ ) );
357
+
358
+ exit();
359
+ }
360
+
361
+ }
Extension_CloudFlare_Popup.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_CloudFlare_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Extension_CloudFlare_Popup();
9
+
10
+ add_action( 'w3tc_ajax_extension_cloudflare_intro',
11
+ array( $o, 'w3tc_ajax_extension_cloudflare_intro' ) );
12
+ add_action( 'w3tc_ajax_extension_cloudflare_intro_done',
13
+ array( $o, 'w3tc_ajax_extension_cloudflare_intro_done' ) );
14
+ add_action( 'w3tc_ajax_extension_cloudflare_zones_done',
15
+ array( $o, 'w3tc_ajax_extension_cloudflare_zones_done' ) );
16
+ }
17
+
18
+
19
+
20
+ public function w3tc_ajax_extension_cloudflare_intro() {
21
+ $c = Dispatcher::config();
22
+
23
+ $details = array(
24
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
25
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) )
26
+ );
27
+
28
+ include W3TC_DIR . '/Extension_CloudFlare_Popup_View_Intro.php';
29
+ exit();
30
+ }
31
+
32
+
33
+
34
+ public function w3tc_ajax_extension_cloudflare_intro_done() {
35
+ $this->_render_extension_cloudflare_zones( array(
36
+ 'email' => $_REQUEST['email'],
37
+ 'key' => $_REQUEST['key'] ) );
38
+ }
39
+
40
+
41
+
42
+ private function _render_extension_cloudflare_zones( $details ) {
43
+ $email = $details['email'];
44
+ $key = $details['key'];
45
+
46
+ $details = array(
47
+ 'email' => $email,
48
+ 'key' => $key
49
+ );
50
+
51
+ try {
52
+ $api = new Extension_CloudFlare_Api( array(
53
+ 'email' => $email,
54
+ 'key' => $key ) );
55
+ $zones = $api->zones();
56
+ } catch ( \Exception $ex ) {
57
+ $details['error_message'] = 'Can\'t authenticate: ' .
58
+ $ex->getMessage();
59
+ include W3TC_DIR . '/Extension_CloudFlare_Popup_View_Intro.php';
60
+ exit();
61
+ }
62
+
63
+ $details['zones'] = $zones;
64
+
65
+ include W3TC_DIR . '/Extension_CloudFlare_Popup_View_Zones.php';
66
+ exit();
67
+ }
68
+
69
+
70
+
71
+ public function w3tc_ajax_extension_cloudflare_zones_done() {
72
+ $email = $_REQUEST['email'];
73
+ $key = $_REQUEST['key'];
74
+ $zone_id = Util_Request::get( 'zone_id' );
75
+
76
+ if ( empty( $zone_id ) ) {
77
+ return $this->_render_extension_cloudflare_zones( array(
78
+ 'email' => $email,
79
+ 'key' => $key,
80
+ 'error_message' => 'Please select zone'
81
+ ) );
82
+ }
83
+
84
+ $zone_name = '';
85
+
86
+ try {
87
+ $api = new Extension_CloudFlare_Api( array(
88
+ 'email' => $email,
89
+ 'key' => $key ) );
90
+ $zones = $api->zones();
91
+ foreach ( $zones as $z ) {
92
+ if ( $z['id'] == $zone_id )
93
+ $zone_name = $z['name'];
94
+ }
95
+ } catch ( \Exception $ex ) {
96
+ $details['error_message'] = 'Can\'t authenticate: ' .
97
+ $ex->getMessage();
98
+ include W3TC_DIR . '/Extension_CloudFlare_Popup_View_Intro.php';
99
+ exit();
100
+ }
101
+
102
+ $c = Dispatcher::config();
103
+
104
+ $c->set( array( 'cloudflare', 'email' ), $email );
105
+ $c->set( array( 'cloudflare', 'key' ), $key );
106
+ $c->set( array( 'cloudflare', 'zone_id' ), $zone_id );
107
+ $c->set( array( 'cloudflare', 'zone_name' ), $zone_name );
108
+ $c->save();
109
+
110
+ delete_transient( 'w3tc_cloudflare_stats' );
111
+
112
+ $postfix = Util_Admin::custom_message_id( array(),
113
+ array(
114
+ 'extension_cloudflare_configuration_saved' =>
115
+ 'CloudFlare credentials are saved successfully' ) );
116
+ echo 'Location admin.php?page=w3tc_extensions&extension=cloudflare&' .
117
+ 'action=view&' . $postfix;
118
+ exit();
119
+ }
120
+ }
Extension_CloudFlare_Popup_View_Intro.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form class="w3tc_extension_cloudflare_form" method="post" style="padding: 20px">
8
+ <?php Util_Ui::hidden( '', 'w3tc_action', 'extension_cloudflare_intro_done' ); ?>
9
+ <?php
10
+ if ( isset( $details['error_message'] ) )
11
+ echo '<div class="error">' . $details['error_message'] . '</div>';
12
+ ?>
13
+ <div class="metabox-holder">
14
+ <?php Util_Ui::postbox_header(
15
+ __( 'Your CloudFlare API key', 'w3-total-cache' ) ); ?>
16
+ <table class="form-table">
17
+ <tr>
18
+ <th><?php _e( 'Email:', 'w3-total-cache' ) ?></td>
19
+ <td>
20
+ <input name="email" type="text" class="w3tc-ignore-change"
21
+ style="width: 300px"
22
+ value="<?php echo esc_attr( $details['email'] ) ?>" />
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <th><?php _e( '<acronym title="Application Programming Interface">API</acronym> key:', 'w3-total-cache' ); ?></td>
27
+ <td>
28
+ <input name="key" type="text" class="w3tc-ignore-change"
29
+ style="width: 550px"
30
+ value="<?php echo esc_attr( $details['key'] ) ?>" />
31
+ </td>
32
+ </tr>
33
+ </table>
34
+
35
+ <p class="submit">
36
+ <input type="button"
37
+ class="w3tc_popup_submit w3tc-button-save button-primary"
38
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
39
+ </p>
40
+ <?php Util_Ui::postbox_footer(); ?>
41
+ </div>
42
+ </form>
Extension_CloudFlare_Popup_View_Zones.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form action="admin.php?page=w3tc_cdn" method="post" style="padding: 20px"
8
+ class="w3tc_extension_cloudflare_form">
9
+ <?php
10
+ Util_Ui::hidden( '', 'w3tc_action', 'extension_cloudflare_zones_done' );
11
+ Util_Ui::hidden( '', 'email', $details['email'] );
12
+ Util_Ui::hidden( '', 'key', $details['key'] );
13
+ echo Util_Ui::nonce_field( 'w3tc' );
14
+
15
+ ?>
16
+ <?php
17
+ if ( isset( $details['error_message'] ) )
18
+ echo '<div class="error">' . $details['error_message'] . '</div>';
19
+ ?>
20
+ <div class="metabox-holder">
21
+ <?php Util_Ui::postbox_header( __( 'Select zone', 'w3-total-cache' ) ); ?>
22
+ <table class="form-table">
23
+ <tr>
24
+ <th>Zone:</td>
25
+ <td>
26
+ <?php foreach ( $details['zones'] as $z ): ?>
27
+ <label>
28
+ <input name="zone_id" type="radio" class="w3tc-ignore-change"
29
+ value="<?php echo $z['id'] ?>" />
30
+ <?php echo htmlspecialchars( $z['name'] ) ?>
31
+ </label><br />
32
+ <?php endforeach ?>
33
+ </tr>
34
+ </table>
35
+
36
+ <p class="submit">
37
+ <input type="button"
38
+ class="w3tc_popup_submit w3tc-button-save button-primary"
39
+ value="<?php _e( 'Next', 'w3-total-cache' ); ?>" />
40
+ </p>
41
+ <?php Util_Ui::postbox_footer(); ?>
42
+ </div>
43
+ </form>
Extension_CloudFlare_SettingsForUi.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_CloudFlare_SettingsForUi {
5
+ static public function api() {
6
+ $c = Dispatcher::config();
7
+ $api = new Extension_CloudFlare_Api( array(
8
+ 'email' => $c->get_string( array( 'cloudflare', 'email' ) ),
9
+ 'key' => $c->get_string( array( 'cloudflare', 'key' ) ),
10
+ 'zone_id' => $c->get_string( array( 'cloudflare', 'zone_id' ) ),
11
+ 'timelimit_api_request' => $c->get_integer(
12
+ array( 'cloudflare', 'timelimit.api_request' ) )
13
+ )
14
+ );
15
+
16
+ return $api;
17
+ }
18
+
19
+
20
+ static public function settings_get( $api ) {
21
+ $settings = $api->zone_settings();
22
+
23
+ // adjust settings that are out of regular presentation
24
+ if ( isset( $settings['security_header'] ) ) {
25
+ $v = $settings['security_header']['value'];
26
+
27
+ $settings['security_header']['editable'] = false;
28
+ $settings['security_header']['value'] = 'off';
29
+ if ( isset( $v['strict_transport_security']['enabled'] ) ) {
30
+ $settings['security_header']['value'] =
31
+ $v['strict_transport_security']['enabled'] ?
32
+ 'on' : 'off';
33
+ }
34
+ }
35
+ if ( isset( $settings['mobile_redirect'] ) ) {
36
+ $v = $settings['mobile_redirect']['value'];
37
+
38
+ $settings['mobile_redirect']['editable'] = false;
39
+ $settings['mobile_redirect']['value'] = 'off';
40
+ if ( isset( $v['status'] ) ) {
41
+ $settings['mobile_redirect']['value'] =
42
+ $v['status'] ? 'on' : 'off';
43
+ }
44
+ }
45
+ if ( isset( $settings['minify'] ) ) {
46
+ $v = $settings['minify']['value'];
47
+
48
+ $editable = $settings['minify']['editable'];
49
+ $settings['minify_js'] = array(
50
+ 'editable' => $editable,
51
+ 'value' => $v['js']
52
+ );
53
+ $settings['minify_css'] = array(
54
+ 'editable' => $editable,
55
+ 'value' => $v['css']
56
+ );
57
+ $settings['minify_html'] = array(
58
+ 'editable' => $editable,
59
+ 'value' => $v['html']
60
+ );
61
+ }
62
+
63
+ return $settings;
64
+ }
65
+
66
+
67
+
68
+ /**
69
+ * Updates settings passed by $_REQUEST
70
+ */
71
+ static public function settings_set( $api ) {
72
+ $errors = array();
73
+ $settings = self::settings_get( $api );
74
+ $to_update = array();
75
+
76
+ $prefix = 'cloudflare_api_';
77
+ foreach ( $_REQUEST as $key => $value ) {
78
+ if ( substr( $key, 0, strlen( $prefix ) ) != $prefix )
79
+ continue;
80
+ if ( $value == '' )
81
+ continue;
82
+ $settings_key = substr( $key, strlen( $prefix ) );
83
+
84
+ if ( !isset( $settings[$settings_key] ) ) {
85
+ $errors[] = 'Option ' . $settings_key . ' is not available';
86
+ continue;
87
+ }
88
+
89
+ $current_value = $settings[$settings_key]['value'];
90
+
91
+ // convert checkbox value to on/off
92
+ // excetion: rocket loader is not checkbox so contains real value
93
+ if ( $settings_key != 'rocket_loader' ) {
94
+ if ( $current_value == 'on' || $current_value == 'off' ) {
95
+ // it's boolean, so control is checkbox - convert it
96
+ $value = ( $value == '0' ? 'off' : 'on' );
97
+ }
98
+ }
99
+
100
+ if ( $current_value == $value )
101
+ continue; // no update required
102
+
103
+ if ( !$settings[$settings_key]['editable'] ) {
104
+ $errors[] = 'Option ' . $settings_key . ' is read-only';
105
+ continue;
106
+ }
107
+
108
+ $to_update[$settings_key] = $value;
109
+ }
110
+
111
+ // mutate settings back to the format of API
112
+ if ( isset( $to_update['minify_js'] ) ||
113
+ isset( $to_update['minify_css'] ) ||
114
+ isset( $to_update['minify_html'] ) ) {
115
+ $v = $settings['minify']['value'];
116
+ if ( isset( $to_update['minify_js'] ) ) {
117
+ $v['js'] = $to_update['minify_js'];
118
+ unset( $to_update['minify_js'] );
119
+ }
120
+ if ( isset( $to_update['minify_css'] ) ) {
121
+ $v['css'] = $to_update['minify_css'];
122
+ unset( $to_update['minify_css'] );
123
+ }
124
+ if ( isset( $to_update['minify_html'] ) ) {
125
+ $v['html'] = $to_update['minify_html'];
126
+ unset( $to_update['minify_html'] );
127
+ }
128
+
129
+ $to_update['minify'] = $v;
130
+ }
131
+
132
+ // do the settings update via API
133
+ foreach ( $to_update as $key => $value ) {
134
+ try {
135
+ $api->zone_setting_set( $key, $value );
136
+ } catch ( \Exception $ex ) {
137
+ $errors[] = 'Failed to update option ' . $key . ': ' .
138
+ $ex->getMessage();
139
+ }
140
+ }
141
+
142
+ return $errors;
143
+ }
144
+ }
Extension_CloudFlare_View_Comments.css ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .cloudflare_ip_check {
2
+ padding-left: 10px;
3
+ }
4
+
5
+ img.cloudflare_ip_check_img {
6
+ float: none;
7
+ vertical-align:middle;
8
+ padding-bottom: 2px;
9
+ }
10
+
11
+ .cloudflare_ip_check_error {
12
+ color: #990000;
13
+ }
14
+
15
+ .cloudflare_ip_check_success {
16
+ color: #009900;
17
+ }
Extension_CloudFlare_View_Comments.js ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ $('.w3tc_cloudflare_ip_check').click(function(e) {
3
+ var href = $(this).attr('href');
4
+ if (href.substr(0, 4) != '#ip=')
5
+ return;
6
+
7
+ e.preventDefault();
8
+
9
+ var ip = unescape(href.substr(4));
10
+ var tr = $(this).parent().closest('tr');
11
+ var ip_span = tr.find('.cloudflare_ip_check');
12
+ if (ip_span.length <= 0) {
13
+ tr.find('.column-author').append(
14
+ '<span class="cloudflare_ip_check"></span>');
15
+ ip_span = tr.find('.cloudflare_ip_check');
16
+ }
17
+
18
+ ip_span.empty();
19
+ $('<img>')
20
+ .attr('class', 'cloudflare_ip_check_img')
21
+ .attr('src', 'images/wpspin_light.gif')
22
+ .attr('alt', 'Resolving IP ' + ip)
23
+ .appendTo(ip_span);
24
+
25
+ jQuery.post(ajaxurl, {
26
+ action:'w3tc_cloudflare_ip_check',
27
+ ip: ip
28
+ }, null, 'json')
29
+ .done(function(data) {
30
+ ip_span.empty();
31
+ var className = (data.error ? 'cloudflare_ip_check_error' :
32
+ 'cloudflare_ip_check_success');
33
+ ip_span.html('<span class="' + className + '">' +
34
+ data.message + '</span>');
35
+ })
36
+ .fail(function() {
37
+ ip_span.html('<span class="cloudflare_ip_check_error">check failed</span>');
38
+ });
39
+ });
40
+ });
41
+
42
+ function w3tc_cloudflare_api_request(action, value, nonce) {
43
+ var email = jQuery('input[id="cloudflare.email"]');
44
+ var key = jQuery('input[id="cloudflare.key"]');
45
+ var zone = jQuery('input[id="cloudflare.zone"]');
46
+
47
+ if (!email.val()) {
48
+ alert('Please enter CloudFlare E-Mail.');
49
+ email.focus();
50
+ return false;
51
+ }
52
+
53
+ if (!key.val()) {
54
+ alert('Please enter CloudFlare API key.');
55
+ key.focus();
56
+ return false;
57
+ }
58
+
59
+ if (!zone.val()) {
60
+ alert('Please enter CloudFlare zone.');
61
+ zone.focus();
62
+ return false;
63
+ }
64
+
65
+ }
Extension_CloudFlare_View_Dashboard.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function() {
2
+ jQuery('#w3tc_dashboard [type=submit]').bind('click', function(){
3
+ jQuery(this).attr('was_clicked','yes');
4
+ });
5
+
6
+ jQuery('#w3tc_dashboard').submit(function(event) {
7
+ var el = jQuery("[was_clicked=yes]").get(0);
8
+ if (el.id == 'flush_all') {
9
+ if (!confirm('Purging your site\'s CloudFlare cache will remove all CloudFlare cache files. It may take up to 48 hours for the CloudFlare cache to completely rebuild on CloudFlare\'s global network. Are you sure you want to purge CloudFlare the cache? Clicking cancel will cancel "empty all caches".')) {
10
+ event.preventDefault();
11
+ }
12
+ }
13
+ jQuery(el).removeAttr("was_clicked");
14
+ });
15
+ });
Extension_CloudFlare_Widget.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * widget with stats
6
+ */
7
+ class Extension_CloudFlare_Widget {
8
+ function init() {
9
+ add_action( 'admin_print_styles-toplevel_page_w3tc_dashboard',
10
+ array( $this, 'admin_print_styles_w3tc_dashboard' ) );
11
+ add_action( 'admin_print_scripts-toplevel_page_w3tc_dashboard',
12
+ array( $this, 'admin_print_scripts_w3tc_dashboard' ) );
13
+
14
+ add_action( 'w3tc_widget_setup', array(
15
+ $this,
16
+ 'w3tc_widget_setup'
17
+ ) );
18
+ }
19
+
20
+
21
+
22
+ function w3tc_widget_setup() {
23
+ Util_Widget::add( 'w3tc_cloudflare', __( 'CloudFlare', 'w3-total-cache' ),
24
+ array( $this, 'widget_form' ),
25
+ Util_Ui::admin_url( 'admin.php?page=w3tc_general#cloudflare' ),
26
+ 'normal' );
27
+ }
28
+
29
+
30
+
31
+ function widget_form() {
32
+ $api = Extension_CloudFlare_SettingsForUi::api();
33
+ $c = Dispatcher::config();
34
+ $interval = $c->get_integer( array( 'cloudflare', 'widget_interval' ) );
35
+
36
+ $v = get_transient( 'w3tc_cloudflare_stats' );
37
+
38
+ try {
39
+ $key = 'dashboard-' . $interval;
40
+ if ( !isset( $v[$key] ) ) {
41
+ if ( !is_array( $v ) )
42
+ $v = array();
43
+
44
+ $v[$key] = $api->analytics_dashboard( $interval );
45
+ set_transient( 'w3tc_cloudflare_stats', $v,
46
+ $this->_cache_mins * 60 );
47
+ }
48
+
49
+ $stats = $v[$key];
50
+ } catch ( \Exception $e ) {
51
+ $stats = null;
52
+ }
53
+
54
+ include W3TC_DIR . '/Extension_CloudFlare_Widget_View.php';
55
+ }
56
+
57
+
58
+
59
+ public function admin_print_styles_w3tc_dashboard() {
60
+ wp_enqueue_style( 'w3tc-widget' );
61
+ wp_enqueue_style( 'w3tc-cloudflare-widget',
62
+ plugins_url( 'Extension_CloudFlare_Widget_View.css', W3TC_FILE ),
63
+ array(), W3TC_VERSION );
64
+ }
65
+
66
+
67
+
68
+ public function admin_print_scripts_w3tc_dashboard() {
69
+ wp_enqueue_script( 'w3tc-metadata' );
70
+ wp_enqueue_script( 'w3tc-widget' );
71
+ }
72
+
73
+
74
+
75
+ private function v( $stats, $key1, $key2 ) {
76
+ echo '<td class="cloudflare_td_value">';
77
+ echo number_format( $this->value( $stats, 'totals', $key1, $key2 ) );
78
+ echo "</td>\n";
79
+ }
80
+
81
+
82
+
83
+ private function value( $a, $k1, $k2, $k3 = null ) {
84
+ $v = $a;
85
+ if ( !is_null( $k1 ) )
86
+ $v = isset( $v[$k1] ) ? $v[$k1] : null;
87
+ if ( !is_null( $k2 ) )
88
+ $v = isset( $v[$k2] ) ? $v[$k2] : null;
89
+ if ( !is_null( $k3 ) )
90
+ $v = isset( $v[$k3] ) ? $v[$k3] : null;
91
+
92
+ return $v;
93
+ }
94
+
95
+
96
+
97
+ private function time( $a, $k1 ) {
98
+ if ( !isset( $a['totals'][$k1] ) )
99
+ return;
100
+
101
+ echo date( 'm/d/Y H:i:s', strtotime( $a['totals'][$k1] ) );
102
+ }
103
+
104
+
105
+
106
+ private function time_mins( $a, $k1 ) {
107
+ if ( !isset( $a['totals'][$k1] ) )
108
+ return;
109
+
110
+ echo date( 'm/d/Y H:i', strtotime( $a['totals'][$k1] ) );
111
+ }
112
+ }
Extension_CloudFlare_Widget_View.css ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .cloudflare_p {
2
+ text-align: center;
3
+ }
4
+
5
+ .cloudflare_table {
6
+ width: 100%;
7
+ padding-left: 20px;
8
+ padding-right: 20px;
9
+ }
10
+
11
+ td.cloudflare_td {
12
+ padding: 0;
13
+ padding-right: 10px;
14
+ font-weight: bold;
15
+ color: #333;
16
+ }
17
+
18
+ td.cloudflare_td_header {
19
+ padding: 0;
20
+ padding-right: 5px;
21
+ padding-top: 5px;
22
+ text-align: right;
23
+ font-weight: bold;
24
+ color: #333;
25
+ }
26
+
27
+ td.cloudflare_td_value {
28
+ padding: 0;
29
+ padding-right: 8px;
30
+ text-align: right;
31
+ color: #333;
32
+ }
Extension_CloudFlare_Widget_View.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php if ( is_null( $stats ) ): ?>
9
+ <?php _e( 'You have not configured well email, API key or domain', 'w3-total-cache' ) ?>
10
+ <?php else: ?>
11
+
12
+ <p class="cloudflare_p">
13
+ Period
14
+ <?php $this->time_mins( $stats, 'since' ) ?>
15
+ -
16
+ <?php $this->time_mins( $stats, 'until' ) ?>
17
+ </p>
18
+ <table class="cloudflare_table">
19
+ <tr>
20
+ <td></td>
21
+ <td class="cloudflare_td_header">All</td>
22
+ <td class="cloudflare_td_header">Cached</td>
23
+ </tr>
24
+ <tr>
25
+ <td class="cloudflare_td">Bandwidth</td>
26
+ <?php $this->v( $stats, 'bandwidth', 'all' ) ?>
27
+ <?php $this->v( $stats, 'bandwidth', 'cached' ) ?>
28
+ </tr>
29
+ <tr>
30
+ <td class="cloudflare_td">Requests</td>
31
+ <?php $this->v( $stats, 'requests', 'all' ) ?>
32
+ <?php $this->v( $stats, 'requests', 'cached' ) ?>
33
+ </tr>
34
+ <tr>
35
+ <td class="cloudflare_td">Page Views</td>
36
+ <?php $this->v( $stats, 'pageviews', 'all' ) ?>
37
+ </tr>
38
+ <tr>
39
+ <td class="cloudflare_td">Uniques</td>
40
+ <?php $this->v( $stats, 'uniques', 'all' ) ?>
41
+ </tr>
42
+ <tr>
43
+ <td class="cloudflare_td">Threats</td>
44
+ <?php $this->v( $stats, 'threats', 'all' ) ?>
45
+ </tr>
46
+ </table>
47
+
48
+ <?php endif; ?>
Extension_FeedBurner_Environment.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ define( 'W3TC_MARKER_BEGIN_FEEDBURNER', '# BEGIN W3TC FeedBurner' );
5
+ define( 'W3TC_MARKER_END_FEEDBURNER', '# END W3TC FeedBurner' );
6
+
7
+ /**
8
+ * adds rules when needed
9
+ */
10
+ class Extension_FeedBurner_Environment {
11
+ /**
12
+ * Fixes environment in each wp-admin request
13
+ *
14
+ * @param Config $config
15
+ * @param bool $force_all_checks
16
+ *
17
+ * @throws Util_Environment_Exceptions
18
+ */
19
+ static public function fix_on_wpadmin_request( $config, $force_all_checks ) {
20
+ $exs = new Util_Environment_Exceptions();
21
+
22
+ if ( $config->get_boolean( 'config.check' ) || $force_all_checks ) {
23
+ $need_rules =
24
+ $config->is_extension_active( 'feedburner' ) &&
25
+ $config->is_extension_active( 'cloudflare' );
26
+
27
+ if ( $need_rules ) {
28
+ self::rules_add( $config, $exs );
29
+ } else {
30
+ self::rules_remove( $exs );
31
+ }
32
+ }
33
+
34
+ if ( count( $exs->exceptions() ) > 0 )
35
+ throw $exs;
36
+ }
37
+
38
+
39
+ static public function deactivate_extension() {
40
+ $exs = new Util_Environment_Exceptions();
41
+ self::rules_remove( $exs );
42
+ }
43
+
44
+
45
+ /**
46
+ * Fixes environment after plugin deactivation
47
+ *
48
+ * @throws Util_Environment_Exceptions
49
+ */
50
+ static public function fix_after_deactivation() {
51
+ $exs = new Util_Environment_Exceptions();
52
+
53
+ self::rules_remove( $exs );
54
+
55
+ if ( count( $exs->exceptions() ) > 0 )
56
+ throw $exs;
57
+ }
58
+
59
+ /**
60
+ * Returns required rules for module
61
+ *
62
+ * @param Config $config
63
+ * @return array
64
+ */
65
+ static public function get_required_rules( $rewrite_rules, $config ) {
66
+ if ( !$config->get_boolean( 'pgcache.enabled' ) ||
67
+ $config->get_string( 'pgcache.engine' ) != 'file_generic' )
68
+ return $rewrite_rules;
69
+
70
+ $pgcache_rules_core_path = Util_Rule::get_pgcache_rules_core_path();
71
+ $rewrite_rules[] = array(
72
+ 'filename' => $pgcache_rules_core_path,
73
+ 'content' => self::rules_generate( $config ),
74
+ 'priority' => 1000
75
+ );
76
+
77
+ return $rewrite_rules;
78
+ }
79
+
80
+
81
+
82
+ /*
83
+ * rules modification
84
+ */
85
+
86
+ /**
87
+ * Writes directives to WP .htaccess
88
+ *
89
+ * @param Config $config
90
+ * @param Util_Environment_Exceptions $exs
91
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
92
+ */
93
+ static private function rules_add( $config, $exs ) {
94
+ return Util_Rule::add_rules( $exs,
95
+ Util_Rule::get_pgcache_rules_core_path(),
96
+ self::rules_generate( $config ),
97
+ W3TC_MARKER_BEGIN_FEEDBURNER,
98
+ W3TC_MARKER_END_FEEDBURNER,
99
+ array(
100
+ W3TC_MARKER_BEGIN_WORDPRESS => 0,
101
+ W3TC_MARKER_END_PGCACHE_CORE =>
102
+ strlen( W3TC_MARKER_END_PGCACHE_CORE ) + 1
103
+ )
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Removes Page Cache core directives
109
+ *
110
+ * @param Util_Environment_Exceptions $exs
111
+ * @throws Util_WpFile_FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials
112
+ */
113
+ static private function rules_remove( $exs ) {
114
+ Util_Rule::remove_rules( $exs, Util_Rule::get_pgcache_rules_core_path(),
115
+ W3TC_MARKER_BEGIN_FEEDBURNER,
116
+ W3TC_MARKER_END_FEEDBURNER
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Generates rules for WP dir
122
+ *
123
+ * @param Config $config
124
+ * @return string
125
+ */
126
+ static private function rules_generate( $config ) {
127
+ switch ( true ) {
128
+ case Util_Environment::is_apache():
129
+ case Util_Environment::is_litespeed():
130
+ return self::rules_generate_apache( $config );
131
+
132
+ case Util_Environment::is_nginx():
133
+ return self::rules_generate_nginx( $config );
134
+ }
135
+
136
+ return '';
137
+ }
138
+
139
+ /**
140
+ * Generates rules for WP dir
141
+ *
142
+ * @param Config $config
143
+ * @return string
144
+ */
145
+ static private function rules_generate_apache( $config ) {
146
+ $rewrite_base = Util_Environment::network_home_url_uri();
147
+
148
+ $a = Util_Environment::wp_upload_dir();
149
+ $parse_url = @parse_url( $a['baseurl'] );
150
+ if ( empty( $parse_url['path'] ) )
151
+ return '';
152
+
153
+ $uploads_path = $parse_url['path'];
154
+
155
+ // cut off rewrite base since its already specified by rules
156
+ if ( substr( $uploads_path, 0, strlen( $rewrite_base ) ) == $rewrite_base )
157
+ $uploads_path_nobase = substr( $uploads_path, strlen( $rewrite_base ) );
158
+ else
159
+ $uploads_path_nobase = $uploads_path;
160
+
161
+ $rules = W3TC_MARKER_BEGIN_FEEDBURNER . "\n";
162
+
163
+ $rules .= "<IfModule mod_rewrite.c>\n";
164
+ $rules .= " RewriteCond %{HTTP_USER_AGENT} FeedBurner\n";
165
+ $rules .= " RewriteRule ^$uploads_path_nobase/([0-9]+)/([0-9]+)/hotlink-ok/(.*)$ $uploads_path/$1/$2/$3 [L]\n";
166
+ $rules .= "</IfModule>\n";
167
+
168
+ $rules .= W3TC_MARKER_END_FEEDBURNER . "\n";
169
+
170
+ return $rules;
171
+ }
172
+
173
+ /**
174
+ * Generates rules for WP dir
175
+ *
176
+ * @param Config $config
177
+ * @return string
178
+ */
179
+ static private function rules_generate_nginx( $config ) {
180
+ $a = Util_Environment::wp_upload_dir();
181
+ $parse_url = @parse_url( $a['baseurl'] );
182
+ if ( empty( $parse_url['path'] ) )
183
+ return '';
184
+
185
+ $uploads_path = $parse_url['path'];
186
+
187
+ $rules = W3TC_MARKER_BEGIN_FEEDBURNER . "\n";
188
+
189
+ $rules .= "if (\$http_user_agent ~* \"(FeedBurner)\") {\n";
190
+ $rules .= " rewrite ^$uploads_path/([0-9]+)/([0-9]+)/hotlink-ok/(.*)$ $uploads_path/$1/$2/$3 last;\n";
191
+ $rules .= "}\n";
192
+
193
+ $rules .= W3TC_MARKER_END_FEEDBURNER . "\n";
194
+
195
+ return $rules;
196
+ }
197
+ }
Extension_FeedBurner_Page.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FeedBurner_Page {
7
+ static public function w3tc_extension_page_feedburner() {
8
+ $config = Dispatcher::config();
9
+ include W3TC_DIR . '/Extension_FeedBurner_Page_View.php';
10
+ }
11
+ }
Extension_FeedBurner_Page_View.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <p>
9
+ Jump to:
10
+ <a href="admin.php?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a> |
11
+ <a href="admin.php?page=w3tc_extensions"><?php _e( 'Extensions', 'w3-total-cache' ); ?></a>
12
+ </p>
13
+ <p>
14
+ FeedBurner extension is currently <?php
15
+ if ( $config->is_extension_active_frontend( 'feedburner' ) )
16
+ echo '<span class="w3tc-enabled">enabled</span>';
17
+ else
18
+ echo '<span class="w3tc-disabled">disabled</span>';
19
+ ?>.
20
+ <p>
21
+
22
+ <div class="metabox-holder">
23
+ <?php Util_Ui::postbox_header( __( 'Google FeedBurner', 'w3-total-cache' ) ); ?>
24
+ <table class="form-table">
25
+ <?php
26
+ Util_Ui::config_item( array(
27
+ 'key' => array( 'feedburner', 'urls' ),
28
+ 'control' => 'textarea',
29
+ 'label' => __( 'Additional URLs:', 'w3-total-cache' ),
30
+ 'description' => __( 'Specify any additional feed URLs to ping on FeedBurner.',
31
+ 'w3-total-cache' )
32
+ ) )
33
+ ?>
34
+ </table>
35
+ <?php Util_Ui::button_config_save( 'extension_feedburner' ); ?>
36
+ <?php Util_Ui::postbox_footer(); ?>
37
+ </div>
Extension_FeedBurner_Plugin.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_FeedBurner_Plugin {
5
+ private $_messagebus_do_ping = false;
6
+
7
+ public function run() {
8
+ add_action( 'w3tc_messagebus_message_received', array(
9
+ $this,
10
+ 'messagebus_message_received'
11
+ ), 20 );
12
+
13
+ // that is called after all flushes happen
14
+ add_action( 'publish_post', array(
15
+ $this,
16
+ 'publish_post'
17
+ ), 1000, 1 );
18
+ }
19
+
20
+
21
+
22
+ /**
23
+ * flush the registered FB feeds
24
+ */
25
+ public function publish_post( $post_id ) {
26
+ $config = Dispatcher::config();
27
+
28
+ if ( Util_Environment::is_flushable_post( $post_id, 'pgcache', $config ) )
29
+ $this->do_ping();
30
+ }
31
+
32
+
33
+
34
+ public function messagebus_message_received() {
35
+ // if we process messagebus message - check if some post has been flushed
36
+ // meaning that real content of responses has changed
37
+ add_action( 'w3tc_flush_post',
38
+ array( $this, 'w3tc_flush_post' ),
39
+ 10100, 1 );
40
+
41
+ add_action( 'w3tc_messagebus_message_processed', array(
42
+ $this,
43
+ 'messagebus_message_processed'
44
+ ), 20 );
45
+ }
46
+
47
+
48
+
49
+ public function w3tc_flush_post( $post_id ) {
50
+ $config = Dispatcher::config();
51
+ if ( Util_Environment::is_flushable_post( $post_id, 'pgcache', $config ) )
52
+ $this->_messagebus_do_ping = true;
53
+ }
54
+
55
+
56
+
57
+ public function messagebus_message_processed() {
58
+ if ( $this->_messagebus_do_ping )
59
+ $this->do_ping();
60
+ }
61
+
62
+
63
+
64
+ private function do_ping() {
65
+ $c = Dispatcher::config();
66
+
67
+ $fb_urls = $c->get_array( array( 'feedburner', 'urls' ) );
68
+
69
+ $fb_urls[] = home_url();
70
+ foreach ( $fb_urls as $url ) {
71
+ if ( !empty( $url ) )
72
+ wp_remote_get( 'http://feedburner.google.com/fb/a/pingSubmit?bloglink=' . urlencode( $url ) );
73
+ }
74
+ }
75
+ }
76
+
77
+
78
+ $p = new Extension_FeedBurner_Plugin();
79
+ $p->run();
80
+
81
+ if ( is_admin() ) {
82
+ $p = new Extension_FeedBurner_Plugin_Admin();
83
+ $p->run();
84
+ }
Extension_FeedBurner_Plugin_Admin.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FeedBurner_Plugin_Admin {
7
+ function run() {
8
+ add_action( 'w3tc_environment_fix_on_wpadmin_request', array(
9
+ '\W3TC\Extension_FeedBurner_Environment', 'fix_on_wpadmin_request' ),
10
+ 10, 2 );
11
+ add_action( 'w3tc_environment_fix_after_deactivation', array(
12
+ '\W3TC\Extension_FeedBurner_Environment', 'fix_after_deactivation' ) );
13
+ add_filter( 'w3tc_environment_get_required_rules', array(
14
+ '\W3TC\Extension_FeedBurner_Environment', 'get_required_rules' ),
15
+ 10, 2 );
16
+
17
+ add_action( 'w3tc_deactivate_extension_feedburner', array(
18
+ '\W3TC\Extension_FeedBurner_Environment', 'deactivate_extension' ) );
19
+
20
+ add_action( 'w3tc_extension_page_feedburner', array(
21
+ '\W3TC\Extension_FeedBurner_Page',
22
+ 'w3tc_extension_page_feedburner'
23
+ ) );
24
+ }
25
+
26
+
27
+
28
+ /**
29
+ *
30
+ *
31
+ * @param unknown $extensions
32
+ * @param Config $config
33
+ * @return mixed
34
+ */
35
+ static public function w3tc_extensions( $extensions, $config ) {
36
+ $message = array();
37
+ $message[] = 'FeedBurner';
38
+
39
+ $extensions['feedburner'] = array (
40
+ 'name' => 'Google FeedBurner',
41
+ 'author' => 'W3 EDGE',
42
+ 'description' => sprintf( __( 'Automatically ping (purge) FeedBurner feeds when pages / posts are modified. Default URL: %s', 'w3-total-cache' ),
43
+ !is_network_admin() ? home_url() : __( 'Network Admin has no main URL.', 'w3-total-cache' ) ),
44
+ 'author_uri' => 'https://www.w3-edge.com/',
45
+ 'extension_uri' => 'https://www.w3-edge.com/',
46
+ 'extension_id' => 'feedburner',
47
+ 'version' => '0.1',
48
+ 'enabled' => true,
49
+ 'requirements' => implode( ', ', $message ),
50
+ 'path' => 'w3-total-cache/Extension_FeedBurner_Plugin.php'
51
+ );
52
+
53
+ return $extensions;
54
+ }
55
+ }
Extension_FragmentCache_Api.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * @param string $fragment_group
5
+ * @param boolean $global If group is for whole network in MS install
6
+ * @return mixed
7
+ */
8
+ function w3tc_fragmentcache_flush_group( $fragment_group ) {
9
+ $o = \W3TC\Dispatcher::component( 'CacheFlush' );
10
+ return $o->fragmentcache_flush_group( $fragment_group );
11
+ }
12
+
13
+ /**
14
+ * Flush all fragment groups
15
+ *
16
+ * @return mixed
17
+ */
18
+ function w3tc_fragmentcache_flush() {
19
+ $o = \W3TC\Dispatcher::component( 'CacheFlush' );
20
+ return $o->fragmentcache_flush();
21
+ }
22
+
23
+ /**
24
+ * Register a fragment group and connected actions for current blog
25
+ *
26
+ * @param string $group
27
+ * @param array $actions on which actions group should be flushed
28
+ * @param integer $expiration in seconds
29
+ * @return mixed
30
+ */
31
+ function w3tc_register_fragment_group( $group, $actions, $expiration ) {
32
+ if ( !is_int( $expiration ) ) {
33
+ $expiration = (int) $expiration;
34
+ trigger_error( __FUNCTION__ . ' needs expiration parameter to be an int.', E_USER_WARNING );
35
+ }
36
+ $o = \W3TC\Dispatcher::component( 'Extension_FragmentCache_Core' );
37
+ return $o->register_group( $group, $actions, $expiration );
38
+ }
39
+
40
+ /**
41
+ * Register a fragment group for whole network in MS install
42
+ *
43
+ * @param unknown $group
44
+ * @param unknown $actions
45
+ * @param integer $expiration in seconds
46
+ * @return mixed
47
+ */
48
+ function w3tc_register_fragment_global_group( $group, $actions, $expiration ) {
49
+ if ( !is_int( $expiration ) ) {
50
+ $expiration = (int) $expiration;
51
+ trigger_error( __FUNCTION__ . ' needs expiration parameter to be an int.', E_USER_WARNING );
52
+ }
53
+ $o = \W3TC\Dispatcher::component( 'Extension_FragmentCache_Core' );
54
+ return $o->register_global_group( $group, $actions,
55
+ $expiration );
56
+ }
57
+
58
+ /**
59
+ * Starts caching output
60
+ *
61
+ * @param string $id the fragment id
62
+ * @param string $group the fragment group name.
63
+ * @param string $hook name of the action/filter hook to disable on fragment found
64
+ * @return bool returns true if cached fragment is echoed
65
+ */
66
+ function w3tc_fragmentcache_start( $id, $group = '', $hook = '' ) {
67
+ $fragment = w3tc_fragmentcache_get( $id, $group );
68
+ if ( false === $fragment ) {
69
+ _w3tc_caching_fragment( $id, $group );
70
+ ob_start();
71
+ } else {
72
+ echo $fragment;
73
+ if ( $hook ) {
74
+ global $wp_filter;
75
+ $wp_filter[$hook] = array();
76
+ }
77
+ return true;
78
+ }
79
+ return false;
80
+ }
81
+
82
+ /**
83
+ * Starts caching filter, returns if filter already cached.
84
+ *
85
+ * @param string $id the fragment id
86
+ * @param string $group the fragment group name.
87
+ * @param string $hook name of the action/filter hook to disable on fragment found
88
+ * @param mixed $data the data returned by the filter
89
+ * @return mixed
90
+ */
91
+ function w3tc_fragmentcache_filter_start( $id, $group = '', $hook = '', $data ) {
92
+ _w3tc_caching_fragment( $id, $group );
93
+ $fragment = w3tc_fragmentcache_get( $id, $group );
94
+ if ( false !== $fragment ) {
95
+ if ( $hook ) {
96
+ global $wp_filter;
97
+ $wp_filter[$hook] = array();
98
+ }
99
+ return $fragment;
100
+ }
101
+ return $data;
102
+ }
103
+
104
+ /**
105
+ * Ends the caching of output. Stores it and outputs the content
106
+ *
107
+ * @param string $id the fragment id
108
+ * @param string $group the fragment group
109
+ * @param bool $debug
110
+ */
111
+ function w3tc_fragmentcache_end( $id, $group = '', $debug = false ) {
112
+ if ( w3tc_is_caching_fragment( $id, $group ) ) {
113
+ $content = ob_get_contents();
114
+ if ( $debug )
115
+ $content = sprintf( "\r\n".'<!-- fragment start (%s%s)-->'."\r\n".'%s'."\r\n".'<!-- fragment end (%1$s%2$s) cached at %s by W3 Total Cache expires in %d seconds -->'."\r\n", $group, $id, $content, date_i18n( 'Y-m-d H:i:s' ), 1000 );
116
+ w3tc_fragmentcache_store( $id, $group, $content );
117
+ ob_end_flush();
118
+ }
119
+ }
120
+
121
+
122
+ /**
123
+ * Ends the caching of filter. Stores it and returns the content
124
+ *
125
+ * @param string $id the fragment id
126
+ * @param string $group the fragment group
127
+ * @param mixed $data
128
+ * @return mixed
129
+ */
130
+ function w3tc_fragmentcache_filter_end( $id, $group = '', $data ) {
131
+ if ( w3tc_is_caching_fragment( $id, $group ) ) {
132
+ w3tc_fragmentcache_store( $id, $group, $data );
133
+ }
134
+ return $data;
135
+ }
136
+
137
+ /**
138
+ * Stores an fragment
139
+ *
140
+ * @param unknown $id
141
+ * @param string $group
142
+ * @param string $content
143
+ */
144
+ function w3tc_fragmentcache_store( $id, $group = '', $content ) {
145
+ set_transient( "{$group}{$id}", $content,
146
+ 1000 /* default expiration in a case its not catched by fc plugin */ );
147
+ }
148
+
149
+ /**
150
+ *
151
+ *
152
+ * @param unknown $id
153
+ * @param string $group
154
+ * @return object
155
+ */
156
+ function w3tc_fragmentcache_get( $id, $group = '' ) {
157
+ return get_transient( "{$group}{$id}" );
158
+ }
159
+
160
+ /**
161
+ * Flushes a fragment from the cache
162
+ *
163
+ * @param unknown $id
164
+ * @param string $group
165
+ */
166
+ function w3tc_fragmentcache_flush_fragment( $id, $group = '' ) {
167
+ delete_transient( "{$group}{$id}" );
168
+ }
169
+
170
+ /**
171
+ * Checks wether page fragment caching is being done for the item
172
+ *
173
+ * @param string $id fragment id
174
+ * @param string $group which group fragment belongs too
175
+ * @return bool
176
+ */
177
+ function w3tc_is_caching_fragment( $id, $group = '' ) {
178
+ global $w3tc_caching_fragment;
179
+ return isset( $w3tc_caching_fragment["{$group}{$id}"] ) &&
180
+ $w3tc_caching_fragment["{$group}{$id}"];
181
+ }
182
+
183
+ /**
184
+ * Internal function, sets if page fragment by $id and $group is being cached
185
+ *
186
+ * @param string $id fragment id
187
+ * @param string $group which group fragment belongs too
188
+ */
189
+ function _w3tc_caching_fragment( $id, $group = '' ) {
190
+ global $w3tc_caching_fragment;
191
+ $w3tc_caching_fragment["{$group}{$id}"] = true;
192
+ }
Extension_FragmentCache_Core.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FragmentCache_Core {
7
+ private $_fragment_groups = array();
8
+ private $_actions = array();
9
+
10
+ /**
11
+ * Register transients group
12
+ *
13
+ * @param unknown $group
14
+ * @param unknown $actions
15
+ * @param unknown $expiration
16
+ */
17
+ function register_group( $group, $actions, $expiration ) {
18
+ return $this->_register_group( $group, $actions, $expiration, false );
19
+ }
20
+
21
+
22
+
23
+ /**
24
+ * Register site-transients group
25
+ *
26
+ * @param string $group
27
+ * @param array $actions
28
+ * @param int $expiration
29
+ */
30
+ function register_global_group( $group, $actions, $expiration ) {
31
+ return $this->_register_group( $group, $actions, $expiration, true );
32
+ }
33
+
34
+
35
+
36
+ private function _register_group( $group, $actions, $expiration, $global ) {
37
+ if ( empty( $group ) )
38
+ return;
39
+
40
+ if ( !is_int( $expiration ) ) {
41
+ $expiration = (int) $expiration;
42
+ trigger_error( __METHOD__ . ' needs expiration parameter to be an int.', E_USER_WARNING );
43
+ }
44
+
45
+ $this->_fragment_groups[$group] = array(
46
+ 'actions' => $actions,
47
+ 'expiration' => $expiration,
48
+ 'global' => $global
49
+ );
50
+
51
+ foreach ( $actions as $action ) {
52
+ if ( !isset( $this->_actions[$action] ) )
53
+ $this->_actions[$action] = array();
54
+ $this->_actions[$action][] = $group;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Returns registered fragment groups, ie transients.
60
+ *
61
+ * @return array array('group' => array('action1','action2'))
62
+ */
63
+ function get_registered_fragment_groups() {
64
+ return $this->_fragment_groups;
65
+ }
66
+
67
+ /**
68
+ * Returns registered actions and transient groups that should be purged per action
69
+ *
70
+ * @return array array('action' => array('group1', 'group2'))
71
+ */
72
+ function get_registered_actions() {
73
+ return $this->_actions;
74
+ }
75
+
76
+ function cleanup() {
77
+ $c = Dispatcher::config();
78
+ $engine = $c->get_string( array( 'fragmentcache', 'engine' ) );
79
+
80
+ switch ( $engine ) {
81
+ case 'file':
82
+ $w3_cache_file_cleaner = new Cache_File_Cleaner( array(
83
+ 'cache_dir' => Util_Environment::cache_blog_dir( 'fragment' ),
84
+ 'clean_timelimit' => $c->get_integer( 'timelimit.cache_gc' )
85
+ ) );
86
+
87
+ $w3_cache_file_cleaner->clean();
88
+ break;
89
+ }
90
+ }
91
+ }
Extension_FragmentCache_Environment.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FragmentCache_Environment {
7
+ /**
8
+ * Fixes environment once event occurs
9
+ *
10
+ * @throws Util_Environment_Exceptions
11
+ */
12
+ static public function fix_on_event( $config, $event, $old_config = null ) {
13
+ if ( $config->get_string( array( 'fragmentcache', 'engine' ) ) == 'file' ) {
14
+ if ( !wp_next_scheduled( 'w3_fragmentcache_cleanup' ) ) {
15
+ wp_schedule_event( time(),
16
+ 'w3_fragmentcache_cleanup',
17
+ 'w3_fragmentcache_cleanup' );
18
+ }
19
+ } else {
20
+ self::unschedule();
21
+ }
22
+ }
23
+
24
+ static public function deactivate_extension() {
25
+ self::unschedule();
26
+ }
27
+
28
+ /**
29
+ * scheduling stuff
30
+ */
31
+ static private function unschedule() {
32
+ if ( wp_next_scheduled( 'w3_fragmentcache_cleanup' ) ) {
33
+ wp_clear_scheduled_hook( 'w3_fragmentcache_cleanup' );
34
+ }
35
+ }
36
+ }
Extension_FragmentCache_GeneralPage.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FragmentCache_GeneralPage {
7
+ /**
8
+ * W3TC General settings page modifications
9
+ */
10
+ static public function admin_init_w3tc_general() {
11
+ $o = new Extension_FragmentCache_GeneralPage();
12
+
13
+ add_filter( 'w3tc_settings_general_anchors',
14
+ array( $o, 'w3tc_settings_general_anchors' ) );
15
+ add_action( 'w3tc_settings_general_boxarea_fragmentcache',
16
+ array( $o, 'w3tc_settings_general_boxarea_fragmentcache' ) );
17
+ }
18
+
19
+
20
+
21
+
22
+ public function w3tc_settings_general_anchors( $anchors ) {
23
+ $anchors[] = array( 'id' => 'fragmentcache', 'text' => 'Fragment Cache' );
24
+ return $anchors;
25
+ }
26
+
27
+
28
+
29
+ public function w3tc_settings_general_boxarea_fragmentcache() {
30
+ include W3TC_DIR . '/Extension_FragmentCache_GeneralPage_View.php';
31
+ }
32
+ }
Extension_FragmentCache_GeneralPage_View.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ Util_Ui::postbox_header( 'Fragment Cache', '', 'fragment_cache' );
8
+ ?>
9
+ <p>Enable fragment caching reduce execution time for common operations.</p>
10
+
11
+ <table class="form-table">
12
+ <?php
13
+ Util_Ui::config_item_engine( array(
14
+ 'key' => array( 'fragmentcache', 'engine' ),
15
+ 'label' => __( 'Fragment Cache Method:', 'w3-total-cache' ),
16
+ 'empty_value' => true
17
+ ) );
18
+ ?>
19
+ </table>
20
+
21
+ <?php
22
+ Util_Ui::button_config_save( 'general_feedburner',
23
+ '<input type="submit" name="w3tc_flush_fragmentcache" value="' .
24
+ __( 'Empty cache', 'w3-total-cache' ) . '" class="button" />' );
25
+ ?>
26
+ <?php Util_Ui::postbox_footer(); ?>
Extension_FragmentCache_Page.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FragmentCache_Page {
7
+ function render_content() {
8
+ $config = Dispatcher::config();
9
+ $core = Dispatcher::component( 'Extension_FragmentCache_Core' );
10
+
11
+ $registered_groups = $core->get_registered_fragment_groups();
12
+ include W3TC_DIR . '/Extension_FragmentCache_Page_View.php';
13
+ }
14
+ }
Extension_FragmentCache_Page_View.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <p id="w3tc-options-menu">
9
+ Jump to:
10
+ <a href="admin.php?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a> |
11
+ <a href="admin.php?page=w3tc_extensions"><?php _e( 'Extensions', 'w3-total-cache' ); ?></a> |
12
+ <a href="#overview"><?php _e( 'Overview', 'w3-total-cache' ); ?></a> |
13
+ <a href="#advanced"><?php _e( 'Advanced', 'w3-total-cache' ); ?></a>
14
+ </p>
15
+ <p>
16
+ Fragment caching via <strong><?php
17
+ echo Cache::engine_name( $config->get_string( array( 'fragmentcache', 'engine' ) ) )
18
+ ?></strong> is currently <?php
19
+ if ( $config->is_extension_active_frontend( 'fragmentcache' ) )
20
+ echo '<span class="w3tc-enabled">enabled</span>';
21
+ else {
22
+ echo '<span class="w3tc-disabled">disabled</span>';
23
+ $ext = Extensions_Util::get_extension( $config, 'fragmentcache' );
24
+
25
+ if ( !empty( $ext['requirements'] ) ) {
26
+ echo ' (<span class="description">' .
27
+ $ext['requirements'] .
28
+ '</span>)';
29
+ }
30
+ }
31
+ ?>.
32
+ <p>
33
+
34
+ <form action="admin.php?page=w3tc_fragmentcache" method="post">
35
+ <p>
36
+ <?php echo Util_Ui::nonce_field( 'w3tc' ); ?>
37
+ <input type="submit" name="w3tc_flush_fragmentcache" value="<?php _e( 'Empty the entire cache', 'w3-total-cache' ) ?>" class="button" />
38
+ <?php _e( 'if needed.', 'w3-total-cache' ) ?>
39
+ </p>
40
+ </form>
41
+
42
+ <form action="admin.php?page=w3tc_fragmentcache" method="post">
43
+ <div class="metabox-holder">
44
+ <?php Util_Ui::postbox_header( __( 'Overview', 'w3-total-cache' ), '', 'overview' ); ?>
45
+ <table class="form-table">
46
+ <tr>
47
+ <th><?php _e( 'Registered fragment groups:', 'w3-total-cache' ); ?></th>
48
+ <td>
49
+ <?php if ( $registered_groups ): ?>
50
+ <ul>
51
+ <?php
52
+ foreach ( $registered_groups as $group => $descriptor )
53
+ echo '<li>', $group,
54
+ ' (', $descriptor['expiration'], ' secs): ',
55
+ implode( ',', $descriptor['actions'] ), '</li>';
56
+ ?>
57
+ </ul>
58
+ <span class="description"><?php _e( 'The groups above will be flushed upon setting changes.', 'w3-total-cache' ); ?></span>
59
+ <?php else: ?>
60
+ <span class="description"><?php _e( 'No groups have been registered.', 'w3-total-cache' ); ?></span>
61
+ <?php endif ?>
62
+ </td>
63
+ </tr>
64
+ </table>
65
+ <?php Util_Ui::postbox_footer(); ?>
66
+
67
+ <?php Util_Ui::postbox_header( __( 'Advanced', 'w3-total-cache' ), '', 'advanced' ); ?>
68
+ <table class="form-table">
69
+ <?php
70
+ if ( $config->get_string( array( 'fragmentcache', 'engine' ) ) == 'memcached' ) {
71
+ $module = 'fragmentcache';
72
+ include W3TC_INC_DIR . '/options/parts/memcached_extension.php';
73
+ } elseif ( $config->get_string( array( 'fragmentcache', 'engine' ) ) == 'redis' ) {
74
+ $module = 'fragmentcache';
75
+ include W3TC_INC_DIR . '/options/parts/redis_extension.php';
76
+ }
77
+ ?>
78
+
79
+ <tr>
80
+ <th style="width: 250px;"><label for="fragmentcache_lifetime"><?php _e( 'Default lifetime of cached fragments:', 'w3-total-cache' ) ?></label></th>
81
+ <td>
82
+ <input id="fragmentcache_lifetime" type="text" <?php Util_Ui::sealing_disabled( 'fragmentcache.' ) ?> name="fragmentcache___lifetime" value="<?php echo esc_attr( $config->get_integer( array( 'fragmentcache', 'lifetime' ) ) ) ?>" size="8" /><?php _e( 'seconds', 'w3-total-cache' ) ?>
83
+ <br /><span class="description"><?php _e( 'Determines the natural expiration time of unchanged cache items. The higher the value, the larger the cache.', 'w3-total-cache' ); ?></span>
84
+ </td>
85
+ </tr>
86
+ <tr>
87
+ <th><label for="fragmentcache_file_gc"><?php _e( 'Garbage collection interval:', 'w3-total-cache' ) ?></label></th>
88
+ <td>
89
+ <input id="fragmentcache_file_gc" type="text" <?php Util_Ui::sealing_disabled( 'fragmentcache.' ) ?> name="fragmentcache___file__gc" value="<?php echo esc_attr( $config->get_integer( array( 'fragmentcache', 'file.gc' ) ) ) ?>" size="8" /> <?php _e( 'seconds', 'w3-total-cache' ) ?>
90
+ <br /><span class="description"><?php _e( 'If caching to disk, specify how frequently expired cache data is removed. For busy sites, a lower value is best.', 'w3-total-cache' ); ?></span>
91
+ </td>
92
+ </tr>
93
+ <tr>
94
+ <th><label for="fragmentcache_groups"><?php _e( 'Manual fragment groups:', 'w3-total-cache' ) ?></label></th>
95
+ <td>
96
+ <textarea id="fragmentcache_groups" name="fragmentcache___groups"
97
+ <?php Util_Ui::sealing_disabled( 'fragmentcache.' ) ?>
98
+ cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $config->get_array( array( 'fragmentcache', 'groups' ) ) ) ); ?></textarea><br />
99
+ <span class="description"><?php _e( 'Specify fragment groups that should be managed by W3 Total Cache. Enter one action per line comma delimited, e.g. (group, action1, action2). Include the prefix used for a transient by a theme or plugin.', 'w3-total-cache' ); ?></span>
100
+ </td>
101
+ </tr>
102
+ </table>
103
+
104
+ <?php Util_Ui::button_config_save( 'extension_fragmentcache' ); ?>
105
+ <?php Util_Ui::postbox_footer(); ?>
106
+ </div>
107
+ </form>
Extension_FragmentCache_Plugin.php ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 FragmentCache plugin
6
+ */
7
+ class Extension_FragmentCache_Plugin {
8
+ private $_config = null;
9
+
10
+ function __construct() {
11
+ $this->_config = Dispatcher::config();
12
+ $this->_core = Dispatcher::component( 'Extension_FragmentCache_Core' );
13
+ }
14
+
15
+ /**
16
+ * Runs plugin
17
+ */
18
+ function run() {
19
+ add_filter( 'w3tc_config_default_values', array(
20
+ $this, 'w3tc_config_default_values' ) );
21
+
22
+ $config = Dispatcher::config();
23
+ // remainder only when extension is frontend-active
24
+ if ( !$config->is_extension_active_frontend( 'fragmentcache' ) )
25
+ return;
26
+
27
+ add_action( 'init', array( $this, 'on_init' ), 9999999 );
28
+
29
+ add_filter( 'cron_schedules', array(
30
+ $this,
31
+ 'cron_schedules'
32
+ ) );
33
+
34
+ add_filter( 'w3tc_footer_comment', array(
35
+ $this,
36
+ 'w3tc_footer_comment'
37
+ ) );
38
+
39
+ if ( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) == 'file' ) {
40
+ add_action( 'w3_fragmentcache_cleanup', array(
41
+ $this,
42
+ 'cleanup'
43
+ ) );
44
+ }
45
+
46
+ add_action( 'switch_blog', array(
47
+ $this,
48
+ 'switch_blog'
49
+ ), 0, 2 );
50
+
51
+ $groups = $this->_config->get_array( array( 'fragmentcache', 'groups' ) );
52
+ foreach ( $groups as $group ) {
53
+ $split = explode( ',', $group );
54
+ $group = array_shift( $split );
55
+ $actions = $split;
56
+ $this->_core->register_group( $group, $actions,
57
+ $this->_config->get_integer( array( 'fragmentcache', 'lifetime' ) ) );
58
+ }
59
+
60
+ // handle transients by own cache
61
+ if ( Util_Environment::is_w3tc_pro( $this->_config ) ) {
62
+ $wp_cache = Dispatcher::component( 'ObjectCache_WpObjectCache' );
63
+ $fc_cache = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
64
+ $wp_cache->register_cache( $fc_cache, array(
65
+ 'transient', 'site-transient' ) );
66
+ }
67
+
68
+ // flush operations
69
+ add_action( 'w3tc_flush_all',
70
+ array( $this, 'w3tc_flush_all' ),
71
+ 300 );
72
+ add_action( 'w3tc_flush_fragmentcache', array(
73
+ $this, 'w3tc_flush_fragmentcache' ) );
74
+ add_action( 'w3tc_flush_fragmentcache_group', array(
75
+ $this, 'w3tc_flush_fragmentcache_group' ), 10, 2 );
76
+
77
+ // usage statistics handling
78
+ add_action( 'w3tc_usage_statistics_of_request', array(
79
+ $this, 'w3tc_usage_statistics_of_request' ), 10, 1 );
80
+ add_filter( 'w3tc_usage_statistics_metrics', array(
81
+ $this, 'w3tc_usage_statistics_metrics' ) );
82
+ }
83
+
84
+
85
+
86
+ public function w3tc_config_default_values( $default_values ) {
87
+ $default_values['fragmentcache'] = array(
88
+ 'file.gc' => 3600,
89
+ 'memcached.servers' => array( '127.0.0.1:11211' ),
90
+ 'memcached.persistent' => true,
91
+ 'redis.persistent' => true,
92
+ 'redis.servers' => array( '127.0.0.1:6379' ),
93
+ 'lifetime' => 180
94
+ );
95
+
96
+ return $default_values;
97
+ }
98
+
99
+
100
+
101
+ function w3tc_flush_all() {
102
+ $cache = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
103
+ $cache->flush();
104
+ }
105
+
106
+
107
+
108
+ function w3tc_flush_fragmentcache() {
109
+ $cache = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
110
+ $cache->flush();
111
+ }
112
+
113
+
114
+
115
+ /**
116
+ * Cleans fragment cache
117
+ */
118
+ function w3tc_flush_fragmentcache_group( $group, $global = false ) {
119
+ $cache = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
120
+ $cache->flush_group( $group, $global );
121
+ }
122
+
123
+
124
+
125
+ /**
126
+ * Does disk cache cleanup
127
+ *
128
+ * @return void
129
+ */
130
+ function cleanup() {
131
+ $this->_core->cleanup();
132
+ }
133
+
134
+
135
+
136
+ /**
137
+ * Cron schedules filter
138
+ *
139
+ * @param array $schedules
140
+ * @return array
141
+ */
142
+ function cron_schedules( $schedules ) {
143
+ $gc_interval = $this->_config->get_integer( array( 'fragmentcache', 'file.gc' ) );
144
+
145
+ return array_merge( $schedules, array(
146
+ 'w3_fragmentcache_cleanup' => array(
147
+ 'interval' => $gc_interval,
148
+ 'display' => sprintf( '[W3TC] Fragment Cache file GC (every %d seconds)', $gc_interval )
149
+ ),
150
+ ) );
151
+ }
152
+
153
+
154
+
155
+ /**
156
+ * Register actions on init
157
+ */
158
+ function on_init() {
159
+ do_action( 'w3tc_register_fragment_groups' );
160
+ $actions = $this->_core->get_registered_actions();
161
+ foreach ( $actions as $action => $groups ) {
162
+ add_action( $action, array( $this, 'on_action' ), 0, 0 );
163
+ }
164
+ }
165
+
166
+
167
+
168
+ /**
169
+ * Flush action
170
+ */
171
+ function on_action() {
172
+ $w3_fragmentcache = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
173
+ $actions = $this->_core->get_registered_actions();
174
+ $action = current_filter();
175
+ $groups = $actions[$action];
176
+ foreach ( $groups as $group ) {
177
+ $w3_fragmentcache->flush_group( $group );
178
+ }
179
+ }
180
+
181
+
182
+
183
+ /**
184
+ * Switch blog action
185
+ */
186
+ function switch_blog( $blog_id, $previous_blog_id ) {
187
+ $o = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
188
+ $o->switch_blog( $blog_id );
189
+ }
190
+
191
+
192
+
193
+ public function w3tc_footer_comment( $strings ) {
194
+ $o = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
195
+ $strings = $o->w3tc_footer_comment( $strings );
196
+
197
+ return $strings;
198
+ }
199
+
200
+
201
+
202
+ public function w3tc_usage_statistics_of_request( $storage ) {
203
+ $o = Dispatcher::component( 'Extension_FragmentCache_WpObjectCache' );
204
+ $o->w3tc_usage_statistics_of_request( $storage );
205
+ }
206
+
207
+
208
+
209
+ public function w3tc_usage_statistics_metrics( $metrics ) {
210
+ return array_merge( $metrics, array(
211
+ 'fragmentcache_calls_total', 'fragmentcache_calls_hits' ) );
212
+ }
213
+ }
214
+
215
+
216
+
217
+ $p = new Extension_FragmentCache_Plugin();
218
+ $p->run();
219
+
220
+ if ( is_admin() ) {
221
+ $p = new Extension_FragmentCache_Plugin_Admin();
222
+ $p->run();
223
+ }
224
+
225
+ include W3TC_DIR . '/Extension_FragmentCache_Api.php';
Extension_FragmentCache_Plugin_Admin.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_FragmentCache_Plugin_Admin {
7
+ private $_config = null;
8
+
9
+
10
+ /**
11
+ * Called from outside, to get extension's details
12
+ */
13
+ static public function w3tc_extensions( $extensions, $config ) {
14
+ $requirements = array();
15
+ if ( !Util_Environment::is_w3tc_pro( $config ) )
16
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
17
+
18
+ $extensions['fragmentcache'] = array (
19
+ 'name' => 'Fragment Cache',
20
+ 'author' => 'W3 EDGE',
21
+ 'description' => 'Caching of page fragments.',
22
+ 'author_uri' => 'https://www.w3-edge.com/',
23
+ 'extension_uri' => 'https://www.w3-edge.com/',
24
+ 'extension_id' => 'fragmentcache',
25
+ 'version' => '1.0',
26
+ 'enabled' => empty( $requirements ),
27
+ 'requirements' => implode( ', ', $requirements ),
28
+ 'active_frontend_own_control' => true,
29
+ 'path' => 'w3-total-cache/Extension_FragmentCache_Plugin.php'
30
+ );
31
+
32
+ return $extensions;
33
+ }
34
+
35
+ function __construct() {
36
+ $this->_config = Dispatcher::config();
37
+ }
38
+
39
+ /**
40
+ * Runs plugin
41
+ */
42
+ function run() {
43
+ add_filter( 'w3tc_objectcache_addin_required', array(
44
+ $this, 'w3tc_objectcache_addin_required' ) );
45
+
46
+ add_action( 'w3tc_environment_fix_on_event', array(
47
+ '\W3TC\Extension_FragmentCache_Environment', 'fix_on_event' ),
48
+ 10, 2 );
49
+ add_action( 'w3tc_deactivate_extension_fragmentcache', array(
50
+ '\W3TC\Extension_FragmentCache_Environment', 'deactivate_extension' ) );
51
+
52
+ add_filter( 'w3tc_admin_menu', array( $this, 'w3tc_admin_menu' ) );
53
+ add_filter( 'w3tc_admin_bar_menu', array( $this, 'w3tc_admin_bar_menu' ) );
54
+ add_filter( 'w3tc_extension_plugin_links_fragmentcache',
55
+ array( $this, 'w3tc_extension_plugin_links' ) );
56
+ add_action( 'w3tc_settings_page-w3tc_fragmentcache',
57
+ array( $this, 'w3tc_settings_page_w3tc_fragmentcache' ) );
58
+
59
+ add_action( 'admin_init_w3tc_general', array(
60
+ '\W3TC\Extension_FragmentCache_GeneralPage',
61
+ 'admin_init_w3tc_general'
62
+ ) );
63
+
64
+ add_action( 'w3tc_config_save', array( $this, 'w3tc_config_save' ), 10, 1 );
65
+
66
+ add_filter( 'w3tc_usage_statistics_summary_from_history', array(
67
+ $this, 'w3tc_usage_statistics_summary_from_history' ), 10, 2 );
68
+ }
69
+
70
+
71
+
72
+ public function w3tc_objectcache_addin_required( $addin_required ) {
73
+ if ( $this->_config->is_extension_active_frontend( 'fragmentcache' ) )
74
+ return true;
75
+
76
+ return $addin_required;
77
+ }
78
+
79
+
80
+
81
+ public function w3tc_extension_plugin_links( $links ) {
82
+ $links = array();
83
+ $links[] = '<a class="edit" href="' .
84
+ esc_attr( Util_Ui::admin_url( 'admin.php?page=w3tc_fragmentcache' ) ) .
85
+ '">'. __( 'Settings' ).'</a>';
86
+
87
+ return $links;
88
+ }
89
+
90
+
91
+
92
+
93
+ public function w3tc_admin_menu( $menu ) {
94
+ $menu = array_merge( $menu, array(
95
+ 'w3tc_fragmentcache' => array(
96
+ 'page_title' => __( 'Fragment Cache', 'w3-total-cache' ),
97
+ 'menu_text' => '<span class="w3tc_menu_item_pro">' .
98
+ __( 'Fragment Cache', 'w3-total-cache' ) . '</span>',
99
+ 'visible_always' => false
100
+ )
101
+ ) );
102
+
103
+ return $menu;
104
+ }
105
+
106
+
107
+
108
+ public function w3tc_admin_bar_menu( $menu_items ) {
109
+ if ( $this->_config->is_extension_active_frontend( 'fragmentcache' ) ) {
110
+ $menu_items['20510.fragmentcache'] = array(
111
+ 'id' => 'w3tc_flush_fragmentcache',
112
+ 'parent' => 'w3tc_flush',
113
+ 'title' => __( 'Fragment Cache: All Fragments', 'w3-total-cache' ),
114
+ 'href' => wp_nonce_url( network_admin_url(
115
+ 'admin.php?page=w3tc_dashboard&amp;w3tc_flush_fragmentcache' ), 'w3tc' )
116
+ );
117
+ }
118
+
119
+ return $menu_items;
120
+ }
121
+
122
+
123
+
124
+ public function w3tc_settings_page_w3tc_fragmentcache() {
125
+ $v = new Extension_FragmentCache_Page();
126
+ $v->render_content();
127
+ }
128
+
129
+
130
+
131
+ public function w3tc_config_save( $config ) {
132
+ // frontend activity
133
+ $engine = $config->get_string( array( 'fragmentcache', 'engine' ) );
134
+
135
+ $is_frontend_active = ( !empty( $engine ) &&
136
+ Util_Environment::is_w3tc_pro( $config ) );
137
+
138
+ $config->set_extension_active_frontend( 'fragmentcache',
139
+ $is_frontend_active );
140
+ }
141
+
142
+
143
+
144
+ public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
145
+ // memcached servers
146
+ $c = Dispatcher::config();
147
+ if ( $c->get_string( array( 'fragmentcache', 'engine' ) ) == 'memcached' ) {
148
+ $summary['memcached_servers']['fragmentcache'] = array(
149
+ 'servers' => $c->get_array( array( 'fragmentcache', 'memcached.servers' ) ),
150
+ 'username' => $c->get_boolean( array( 'fragmentcache', 'memcached.username' ) ),
151
+ 'password' => $c->get_boolean( array( 'fragmentcache', 'memcached.password' ) ),
152
+ 'name' => __( 'Fragment Cache', 'w3-total-cache' )
153
+ );
154
+ } elseif ( $c->get_string( array( 'fragmentcache', 'engine' ) ) == 'redis' ) {
155
+ $summary['redis_servers']['fragmentcache'] = array(
156
+ 'servers' => $c->get_array( array( 'fragmentcache', 'redis.servers' ) ),
157
+ 'username' => $c->get_boolean( array( 'fragmentcache', 'redis.username' ) ),
158
+ 'dbid' => $c->get_boolean( array( 'fragmentcache', 'redis.dbid' ) ),
159
+ 'password' => $c->get_boolean( array( 'fragmentcache', 'redis.password' ) ),
160
+ 'name' => __( 'Fragment Cache', 'w3-total-cache' )
161
+ );
162
+ }
163
+
164
+ // counters
165
+ $fragmentcache_calls_total = Util_UsageStatistics::sum( $history,
166
+ 'fragmentcache_calls_total' );
167
+ $fragmentcache_calls_hits = Util_UsageStatistics::sum( $history,
168
+ 'fragmentcache_calls_hits' );
169
+
170
+ $summary['fragmentcache'] = array(
171
+ 'calls_total' => Util_UsageStatistics::integer(
172
+ $fragmentcache_calls_total ),
173
+ 'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
174
+ $fragmentcache_calls_total, $summary ),
175
+ 'hit_rate' => Util_UsageStatistics::percent(
176
+ $fragmentcache_calls_total, $fragmentcache_calls_total )
177
+ );
178
+
179
+ return $summary;
180
+ }
181
+ }
Extension_FragmentCache_WpObjectCache.php ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Fragment Cache object
6
+ */
7
+ class Extension_FragmentCache_WpObjectCache {
8
+ /**
9
+ * Internal cache array
10
+ *
11
+ * @var array
12
+ */
13
+ var $cache = array();
14
+
15
+ /**
16
+ * Array of global groups
17
+ *
18
+ * @var array
19
+ */
20
+ var $global_groups = array( 'site-transient' );
21
+
22
+ /**
23
+ * List of non-persistent groups
24
+ *
25
+ * @var array
26
+ */
27
+ var $nonpersistent_groups = array();
28
+
29
+ /**
30
+ * Total count of calls
31
+ *
32
+ * @var integer
33
+ */
34
+ var $cache_total = 0;
35
+
36
+ /**
37
+ * Cache hits count
38
+ *
39
+ * @var integer
40
+ */
41
+ var $cache_hits = 0;
42
+
43
+ /**
44
+ * Cache misses count
45
+ *
46
+ * @var integer
47
+ */
48
+ var $cache_misses = 0;
49
+
50
+ /**
51
+ * Total time
52
+ *
53
+ * @var integer
54
+ */
55
+ var $time_total = 0;
56
+
57
+ /**
58
+ * Store debug information of w3tc using
59
+ *
60
+ * @var array
61
+ */
62
+ var $debug_info = array();
63
+
64
+ /**
65
+ * Blog id of cache
66
+ *
67
+ * @var integer
68
+ */
69
+ private $_blog_id;
70
+
71
+ /**
72
+ * Key cache
73
+ *
74
+ * @var array
75
+ */
76
+ var $_key_cache = array();
77
+
78
+ /**
79
+ * Config
80
+ */
81
+ var $_config = null;
82
+
83
+ /**
84
+ * Caching flag
85
+ *
86
+ * @var boolean
87
+ */
88
+ var $_caching = false;
89
+
90
+ /**
91
+ * Cache reject reason
92
+ *
93
+ * @var string
94
+ */
95
+ var $cache_reject_reason = '';
96
+
97
+ /**
98
+ * Lifetime
99
+ *
100
+ * @var integer
101
+ */
102
+ var $_lifetime = 0;
103
+
104
+ /**
105
+ * Debug flag
106
+ *
107
+ * @var boolean
108
+ */
109
+ var $_debug = false;
110
+
111
+ private $_core;
112
+
113
+ /**
114
+ * PHP5 style constructor
115
+ */
116
+ function __construct() {
117
+ global $_wp_using_ext_object_cache;
118
+
119
+ $this->_config = Dispatcher::config();
120
+ $this->_lifetime = $this->_config->get_integer( array( 'fragmentcache', 'lifetime' ) );
121
+ $this->_debug = $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) );
122
+ $this->_caching = $_wp_using_ext_object_cache = $this->_can_cache();
123
+
124
+ $this->_blog_id = Util_Environment::blog_id();
125
+ $this->_core = Dispatcher::component( 'Extension_FragmentCache_Core' );
126
+ }
127
+
128
+ /**
129
+ * Get from the cache
130
+ *
131
+ * @param string $id
132
+ * @param string $group
133
+ * @return mixed
134
+ */
135
+ function get( $id, $group = 'transient', $force = false, &$found = null ) {
136
+ if ( $this->_debug ) {
137
+ $time_start = Util_Debug::microtime();
138
+ }
139
+
140
+ $key = $this->_get_cache_key( $id );
141
+ list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
142
+ $this->_fragment_group( $id, $group );
143
+ $internal = isset( $this->cache[$fragment_group][$key] );
144
+
145
+ if ( $internal ) {
146
+ $found = true;
147
+ $value = $this->cache[$fragment_group][$key];
148
+ } elseif ( $this->_caching &&
149
+ !in_array( $group, $this->nonpersistent_groups ) ) {
150
+ $cache = $this->_get_cache( $fragment_group_global );
151
+ $v = $cache->get( $key, $fragment_group );
152
+ if ( is_array( $v ) && $v['content'] != null ) {
153
+ $found = true;
154
+ $value = $v['content'];
155
+ } else
156
+ $value = false;
157
+ } else {
158
+ $value = false;
159
+ }
160
+
161
+ if ( $value === null ) {
162
+ $value = false;
163
+ }
164
+
165
+ if ( is_object( $value ) ) {
166
+ $value = clone( $value );
167
+ }
168
+
169
+ $this->cache[$fragment_group][$key] = $value;
170
+ $this->cache_total++;
171
+
172
+ if ( $value !== false ) {
173
+ $cached = true;
174
+ $this->cache_hits++;
175
+ } else {
176
+ $cached = false;
177
+ $this->cache_misses++;
178
+ }
179
+
180
+ /**
181
+ * Add debug info
182
+ */
183
+ if ( $this->_debug ) {
184
+ $time = Util_Debug::microtime() - $time_start;
185
+ $this->time_total += $time;
186
+
187
+ if ( !$group ) {
188
+ $group = 'transient';
189
+ }
190
+
191
+ $this->debug_info[] = array(
192
+ 'id' => $id,
193
+ 'group' => $group,
194
+ 'cached' => $cached,
195
+ 'internal' => $internal,
196
+ 'data_size' => ( $value ? strlen( serialize( $value ) ) : '' ),
197
+ 'time' => $time
198
+ );
199
+ }
200
+
201
+ return $value;
202
+ }
203
+
204
+ /**
205
+ * Set to the cache
206
+ *
207
+ * @param string $id
208
+ * @param mixed $data
209
+ * @param string $group
210
+ * @param integer $expire
211
+ * @return boolean
212
+ */
213
+ function set( $id, $data, $group = 'transient', $expire = 0 ) {
214
+ $key = $this->_get_cache_key( $id );
215
+
216
+ if ( is_object( $data ) ) {
217
+ $data = clone( $data );
218
+ }
219
+
220
+ list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
221
+ $this->_fragment_group( $id, $group );
222
+ if ( is_int( $fragment_group_expiration ) )
223
+ $expire = $fragment_group_expiration;
224
+
225
+ $this->cache[$fragment_group][$key] = $data;
226
+
227
+ if ( $this->_caching &&
228
+ !in_array( $group, $this->nonpersistent_groups ) ) {
229
+ $cache = $this->_get_cache( $fragment_group_global );
230
+
231
+ $v = array( 'content' => $data );
232
+ return $cache->set( $key, $v,
233
+ ( $expire ? $expire : $this->_lifetime ), $fragment_group );
234
+ }
235
+
236
+ return true;
237
+ }
238
+
239
+ /**
240
+ * Delete from the cache
241
+ *
242
+ * @param string $id
243
+ * @param string $group
244
+ * @param bool $force
245
+ * @return boolean
246
+ */
247
+ function delete( $id, $group = 'transient', $force = false ) {
248
+ if ( !$force && $this->get( $id, $group ) === false ) {
249
+ return false;
250
+ }
251
+ list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
252
+ $this->_fragment_group( $id, $group );
253
+
254
+ $key = $this->_get_cache_key( $id );
255
+
256
+ unset( $this->cache[$fragment_group][$key] );
257
+
258
+ if ( $this->_caching && !in_array( $group, $this->nonpersistent_groups ) ) {
259
+ $cache = $this->_get_cache( $fragment_group_global );
260
+ return $cache->delete( $key, $fragment_group );
261
+ }
262
+
263
+ return true;
264
+ }
265
+
266
+ /**
267
+ * Add to the cache
268
+ *
269
+ * @param string $id
270
+ * @param mixed $data
271
+ * @param string $group
272
+ * @param integer $expire
273
+ * @return boolean
274
+ */
275
+ function add( $id, $data, $group = 'default', $expire = 0 ) {
276
+ if ( $this->get( $id, $group ) !== false ) {
277
+ return false;
278
+ }
279
+
280
+ return $this->set( $id, $data, $group, $expire );
281
+ }
282
+
283
+ /**
284
+ * Replace in the cache
285
+ *
286
+ * @param string $id
287
+ * @param mixed $data
288
+ * @param string $group
289
+ * @param integer $expire
290
+ * @return boolean
291
+ */
292
+ function replace( $id, $data, $group = 'default', $expire = 0 ) {
293
+ if ( $this->get( $id, $group ) === false ) {
294
+ return false;
295
+ }
296
+
297
+ return $this->set( $id, $data, $group, $expire );
298
+ }
299
+
300
+ /**
301
+ * Reset keys
302
+ *
303
+ * @return boolean
304
+ */
305
+ function reset() {
306
+ global $_wp_using_ext_object_cache;
307
+
308
+ $_wp_using_ext_object_cache = $this->_caching;
309
+
310
+ return true;
311
+ }
312
+
313
+ /**
314
+ * Flush cache
315
+ *
316
+ * @return boolean
317
+ */
318
+ function flush() {
319
+ $this->cache = array();
320
+
321
+ $cache = $this->_get_cache( false );
322
+ $cache->flush( 'nogroup' );
323
+
324
+ $cache = $this->_get_cache( true );
325
+ $cache->flush( 'global-nogroup' );
326
+
327
+ $groups = $this->_core->get_registered_fragment_groups();
328
+ foreach ( $groups as $group => $descriptor ) {
329
+ $cache = $this->_get_cache( $descriptor['global'] );
330
+ $cache->flush( $group );
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Purges all transients that belong to a transient group
336
+ *
337
+ * @param string $fragment_group fragment grouping
338
+ * @param bool $global whether flush site group or network wide, default is site specific. Use true for network wide.
339
+ * @return bool
340
+ */
341
+ function flush_group( $fragment_group ) {
342
+ unset( $this->cache[$fragment_group] );
343
+
344
+ if ( $this->_caching ) {
345
+ list( $f1, $f2, $fragment_group_global ) =
346
+ $this->_fragment_group( $fragment_group, '' );
347
+
348
+ $cache = $this->_get_cache( $fragment_group_global );
349
+ return $cache->flush( $fragment_group );
350
+ }
351
+ return true;
352
+ }
353
+
354
+ /**
355
+ * Add global groups
356
+ *
357
+ * @param array $groups
358
+ * @return void
359
+ */
360
+ function add_global_groups( $groups ) {
361
+ if ( !is_array( $groups ) ) {
362
+ $groups = (array) $groups;
363
+ }
364
+
365
+ $this->global_groups = array_merge( $this->global_groups, $groups );
366
+ $this->global_groups = array_unique( $this->global_groups );
367
+ }
368
+
369
+ /**
370
+ * Add non-persistent groups
371
+ *
372
+ * @param array $groups
373
+ * @return void
374
+ */
375
+ function add_nonpersistent_groups( $groups ) {
376
+ if ( !is_array( $groups ) ) {
377
+ $groups = (array) $groups;
378
+ }
379
+
380
+ $this->nonpersistent_groups = array_merge( $this->nonpersistent_groups, $groups );
381
+ $this->nonpersistent_groups = array_unique( $this->nonpersistent_groups );
382
+ }
383
+
384
+ /**
385
+ * Increment numeric cache item's value
386
+ *
387
+ * @param int|string $key The cache key to increment
388
+ * @param int $offset The amount by which to increment the item's value. Default is 1.
389
+ * @param string $group The group the key is in.
390
+ * @return bool|int False on failure, the item's new value on success.
391
+ */
392
+ function incr( $key, $offset = 1, $group = 'default' ) {
393
+ $value = $this->get( $key, $group );
394
+ if ( $value === false )
395
+ return false;
396
+
397
+ if ( !is_numeric( $value ) )
398
+ $value = 0;
399
+
400
+ $offset = (int) $offset;
401
+ $value += $offset;
402
+
403
+ if ( $value < 0 )
404
+ $value = 0;
405
+ $this->replace( $key, $value, $group );
406
+ return $value;
407
+ }
408
+
409
+ /**
410
+ * Decrement numeric cache item's value
411
+ *
412
+ * @param int|string $key The cache key to increment
413
+ * @param int $offset The amount by which to decrement the item's value. Default is 1.
414
+ * @param string $group The group the key is in.
415
+ * @return bool|int False on failure, the item's new value on success.
416
+ */
417
+ function decr( $key, $offset = 1, $group = 'default' ) {
418
+ $value = $this->get( $key, $group );
419
+ if ( $value === false )
420
+ return false;
421
+
422
+ if ( !is_numeric( $value ) )
423
+ $value = 0;
424
+
425
+ $offset = (int) $offset;
426
+ $value -= $offset;
427
+
428
+ if ( $value < 0 )
429
+ $value = 0;
430
+ $this->replace( $key, $value, $group );
431
+ return $value;
432
+ }
433
+
434
+ /**
435
+ * Print Fragment Cache stats
436
+ *
437
+ * @return void
438
+ */
439
+ function stats() {
440
+ echo '<h2>Summary</h2>';
441
+ echo '<p>';
442
+ echo '<strong>Engine</strong>: ' . Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ) . '<br />';
443
+ echo '<strong>Caching</strong>: ' . ( $this->_caching ? 'enabled' : 'disabled' ) . '<br />';
444
+
445
+ if ( !$this->_caching ) {
446
+ echo '<strong>Reject reason</strong>: ' . $this->cache_reject_reason . '<br />';
447
+ }
448
+
449
+ echo '<strong>Total calls</strong>: ' . $this->cache_total . '<br />';
450
+ echo '<strong>Cache hits</strong>: ' . $this->cache_hits . '<br />';
451
+ echo '<strong>Cache misses</strong>: ' . $this->cache_misses . '<br />';
452
+ echo '<strong>Total time</strong>: '. round( $this->time_total, 4 ) . 's';
453
+ echo '</p>';
454
+
455
+ echo '<h2>Cache info</h2>';
456
+
457
+ if ( $this->_debug ) {
458
+ echo '<table cellpadding="0" cellspacing="3" border="1">';
459
+ echo '<tr><td>#</td><td>Status</td><td>Source</td><td>Data size (b)</td><td>Query time (s)</td><td>ID:Group</td></tr>';
460
+
461
+ foreach ( $this->debug_info as $index => $debug ) {
462
+ echo '<tr>';
463
+ echo '<td>' . ( $index + 1 ) . '</td>';
464
+ echo '<td>' . ( $debug['cached'] ? 'cached' : 'not cached' ) . '</td>';
465
+ echo '<td>' . ( $debug['internal'] ? 'internal' : 'persistent' ) . '</td>';
466
+ echo '<td>' . $debug['data_size'] . '</td>';
467
+ echo '<td>' . round( $debug['time'], 4 ) . '</td>';
468
+ echo '<td>' . sprintf( '%s:%s', $debug['id'], $debug['group'] ) . '</td>';
469
+ echo '</tr>';
470
+ }
471
+
472
+ echo '</table>';
473
+ } else {
474
+ echo '<p>Enable debug mode.</p>';
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Switches context to another blog
480
+ *
481
+ * @param integer $blog_id
482
+ */
483
+ function switch_blog( $blog_id ) {
484
+ $this->reset();
485
+ $this->_blog_id = $blog_id;
486
+ }
487
+
488
+ /**
489
+ * Returns cache key
490
+ *
491
+ * @param string $id
492
+ * @param string $group
493
+ * @return string
494
+ */
495
+ function _get_cache_key( $id ) {
496
+ return md5( $id );
497
+ }
498
+
499
+ /**
500
+ * Returns cache object
501
+ *
502
+ * @param null $blog_id
503
+ * @param string $group
504
+ * @return W3_Cache_Base
505
+ */
506
+ function _get_cache( $global = false ) {
507
+ static $cache = array();
508
+
509
+ if ( !$global )
510
+ $blog_id = $this->_blog_id;
511
+ else
512
+ $blog_id = 0;
513
+
514
+ if ( !isset( $cache[$blog_id] ) ) {
515
+ $engine = $this->_config->get_string( array( 'fragmentcache', 'engine' ) );
516
+
517
+ switch ( $engine ) {
518
+ case 'memcached':
519
+ $engineConfig = array(
520
+ 'servers' => $this->_config->get_array( array( 'fragmentcache', 'memcached.servers' ) ),
521
+ 'persistent' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.persistent' ) ),
522
+ 'aws_autodiscovery' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.aws_autodiscovery' ) ),
523
+ 'username' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.username' ) ),
524
+ 'password' => $this->_config->get_boolean( array( 'fragmentcache', 'memcached.password' ) )
525
+ );
526
+ break;
527
+
528
+ case 'redis':
529
+ $engineConfig = array(
530
+ 'servers' => $this->_config->get_array( array( 'fragmentcache', 'redis.servers' ) ),
531
+ 'persistent' => $this->_config->get_boolean( array( 'fragmentcache', 'redis.persistent' ) ),
532
+ 'dbid' => $this->_config->get_boolean( array( 'fragmentcache', 'redis.dbid' ) ),
533
+ 'password' => $this->_config->get_boolean( array( 'fragmentcache', 'redis.password' ) )
534
+ );
535
+ break;
536
+
537
+ case 'file':
538
+ $engineConfig = array(
539
+ 'section' => 'fragment',
540
+ 'locking' => $this->_config->get_boolean( array( 'fragmentcache', 'file.locking' ) ),
541
+ 'flush_timelimit' => $this->_config->get_integer( 'timelimit.cache_flush' )
542
+ );
543
+ break;
544
+
545
+ default:
546
+ $engineConfig = array();
547
+ }
548
+ $engineConfig['blog_id'] = $blog_id;
549
+ $engineConfig['module'] = 'fragmentcache';
550
+ $engineConfig['host'] = Util_Environment::host();
551
+ $engineConfig['instance_id'] = Util_Environment::instance_id();
552
+
553
+ $cache[$blog_id] = Cache::instance( $engine, $engineConfig );
554
+ }
555
+
556
+ return $cache[$blog_id];
557
+ }
558
+
559
+ /**
560
+ * Check if caching allowed on init
561
+ *
562
+ * @return boolean
563
+ */
564
+ function _can_cache() {
565
+ return true;
566
+ }
567
+
568
+ /**
569
+ * Returns debug info
570
+ *
571
+ * @return string
572
+ */
573
+ public function w3tc_footer_comment( $strings ) {
574
+ if ( $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) ) ) {
575
+ $strings[] = "Fragment Cache debug info:";
576
+ $strings[] = sprintf( "%s%s", str_pad( 'Engine: ', 20 ), Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ) );
577
+ $strings[] = sprintf( "%s%s", str_pad( 'Caching: ', 20 ), ( $this->_caching ? 'enabled' : 'disabled' ) );
578
+
579
+ if ( !$this->_caching ) {
580
+ $strings[] = sprintf( "%s%s", str_pad( 'Reject reason: ', 20 ), $this->cache_reject_reason );
581
+ }
582
+
583
+ $strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total );
584
+ $strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits );
585
+ $strings[] = sprintf( "%s%d", str_pad( 'Cache misses: ', 20 ), $this->cache_misses );
586
+ $strings[] = sprintf( "%s%.4f", str_pad( 'Total time: ', 20 ), $this->time_total );
587
+
588
+ $strings[] = "W3TC Fragment Cache info:";
589
+ $strings[] = sprintf( "%s | %s | %s | %s | %s | %s| %s| %s",
590
+ str_pad( '#', 5, ' ', STR_PAD_LEFT ),
591
+ str_pad( 'Status', 15, ' ', STR_PAD_BOTH ),
592
+ str_pad( 'Source', 15, ' ', STR_PAD_BOTH ),
593
+ str_pad( 'Data size (b)', 13, ' ', STR_PAD_LEFT ),
594
+ str_pad( 'Query time (s)', 14, ' ', STR_PAD_LEFT ),
595
+ str_pad( 'Group', 14, ' ', STR_PAD_LEFT ),
596
+ str_pad( 'Accessible', 10, ' ', STR_PAD_LEFT ),
597
+ 'Transient ID' );
598
+
599
+ foreach ( $this->debug_info as $index => $debug ) {
600
+ list( $fragment_group, $fragment_group_expiration, $fragment_group_global ) =
601
+ $this->_fragment_group( $debug['id'], $debug['group'] );
602
+ $strings[] = sprintf( "%s | %s | %s | %s | %s | %s| %s| %s",
603
+ str_pad( $index + 1, 5, ' ', STR_PAD_LEFT ),
604
+ str_pad( ( $debug['cached'] ? 'cached' : 'not cached' ), 15, ' ', STR_PAD_BOTH ),
605
+ str_pad( ( $debug['internal'] ? 'internal' : 'persistent' ), 15, ' ', STR_PAD_BOTH ),
606
+ str_pad( $debug['data_size'], 13, ' ', STR_PAD_LEFT ),
607
+ str_pad( round( $debug['time'], 4 ), 14, ' ', STR_PAD_LEFT ),
608
+ str_pad( $fragment_group, 14, ' ', STR_PAD_LEFT ),
609
+ str_pad( ( $debug['group'] == 'transient' ? 'site' : 'network' ), 10, ' ', STR_PAD_LEFT ),
610
+ $debug['id'] );
611
+ }
612
+ } else {
613
+ $append = ( $this->cache_reject_reason != '' ?
614
+ sprintf( ' (%s)', $this->cache_reject_reason ) :'' );
615
+
616
+ $strings[] = sprintf(
617
+ __( 'Fragment Caching %d/%d fragments using %s%s', 'w3-total-cache' ),
618
+ $this->cache_hits, $this->cache_total,
619
+ Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ),
620
+ $append );
621
+ }
622
+
623
+ return $strings;
624
+ }
625
+
626
+ /**
627
+ * Returns the group part of a transient/site-transient id.
628
+ * Uses registered fragment groups to identify it.
629
+ *
630
+ * @param unknown $id
631
+ * @return string
632
+ */
633
+ private function _fragment_group( $id, $group ) {
634
+ if ( empty( $id ) )
635
+ return array( 'nogroup', 0 );
636
+ $groups = $this->_core->get_registered_fragment_groups();
637
+ $use_group = '';
638
+ $length = 0;
639
+ foreach ( $groups as $group => $descriptor ) {
640
+ if ( strpos( $id, $group ) !== false ) {
641
+ if ( strlen( $group ) > $length ) {
642
+ $length = strlen( $group );
643
+ $use_group = array( $group, $descriptor['expiration'],
644
+ $descriptor['global'] );
645
+ }
646
+ }
647
+ }
648
+ if ( $use_group )
649
+ return $use_group;
650
+
651
+ if ( $group == 'site-transient' )
652
+ return array( 'global-nogroup', 0, true );
653
+
654
+ return array( 'nogroup', 0, false );
655
+ }
656
+
657
+ public function w3tc_usage_statistics_of_request( $storage ) {
658
+ $storage->counter_add( 'fragmentcache_calls_total', $this->cache_total );
659
+ $storage->counter_add( 'fragmentcache_calls_hits', $this->cache_hits );
660
+ }
661
+ }
Extension_Genesis_Page.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_Genesis_Page {
7
+ static public function w3tc_extension_page_genesis_theme() {
8
+ $config = Dispatcher::config();
9
+ include W3TC_DIR . '/Extension_Genesis_Page_View.php';
10
+ }
11
+ }
Extension_Genesis_Page_View.php ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <p id="w3tc-options-menu">
9
+ Jump to:
10
+ <a href="admin.php?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a> |
11
+ <a href="admin.php?page=w3tc_extensions"><?php _e( 'Extensions', 'w3-total-cache' ); ?></a> |
12
+ <a href="#header"><?php _e( 'Header', 'w3-total-cache' ); ?></a> |
13
+ <a href="#content"><?php _e( 'Content', 'w3-total-cache' ); ?></a> |
14
+ <a href="#sidebar"><?php _e( 'Sidebar', 'w3-total-cache' ); ?></a> |
15
+ <a href="#exclusions"><?php _e( 'Exclusions', 'w3-total-cache' ); ?></a>
16
+ </p>
17
+ <p>
18
+ Genesis extension is currently <?php
19
+ if ( $config->is_extension_active_frontend( 'genesis.theme' ) )
20
+ echo '<span class="w3tc-enabled">enabled</span>';
21
+ else
22
+ echo '<span class="w3tc-disabled">disabled</span>';
23
+ ?>.
24
+ <p>
25
+
26
+ <div class="metabox-holder">
27
+ <?php Util_Ui::postbox_header( __( 'Header', 'w3-total-cache' ), '', 'header' ); ?>
28
+ <table class="form-table">
29
+ <?php
30
+ Util_Ui::config_item( array(
31
+ 'key' => array( 'genesis.theme', 'wp_head' ),
32
+ 'control' => 'checkbox',
33
+ 'label' => __( 'Cache wp_head loop:', 'w3-total-cache' ),
34
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
35
+ 'description' =>__( 'Cache wp_head. This includes the embedded CSS, JS etc.', 'w3-total-cache' )
36
+ ) );
37
+ Util_Ui::config_item( array(
38
+ 'key' => array( 'genesis.theme', 'genesis_header' ),
39
+ 'control' => 'checkbox',
40
+ 'label' => __( 'Cache header:', 'w3-total-cache' ),
41
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
42
+ 'description' => __( 'Cache header loop. This is the area where the logo is located.', 'w3-total-cache' )
43
+ ) );
44
+ Util_Ui::config_item( array(
45
+ 'key' => array( 'genesis.theme', 'genesis_do_nav' ),
46
+ 'control' => 'checkbox',
47
+ 'label' => __( 'Cache primary navigation:', 'w3-total-cache' ),
48
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
49
+ 'description' => __( 'Caches the navigation filter; per page.', 'w3-total-cache' )
50
+ ) );
51
+ Util_Ui::config_item( array(
52
+ 'key' => array( 'genesis.theme', 'genesis_do_subnav' ),
53
+ 'control' => 'checkbox',
54
+ 'label' => __( 'Cache secondary navigation:', 'w3-total-cache' ),
55
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
56
+ 'description' => __( 'Caches secondary navigation filter; per page.', 'w3-total-cache' )
57
+ ) );
58
+
59
+ ?>
60
+ </table>
61
+ <?php Util_Ui::button_config_save( 'extension_genesis_header' ); ?>
62
+ <?php Util_Ui::postbox_footer(); ?>
63
+
64
+
65
+
66
+ <?php Util_Ui::postbox_header( __( 'Content', 'w3-total-cache' ), '', 'content' ); ?>
67
+ <table class="form-table">
68
+ <?php
69
+ Util_Ui::config_item( array(
70
+ 'key' => array( 'genesis.theme', 'loop_front_page' ),
71
+ 'control' => 'checkbox',
72
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
73
+ 'label' => __( 'Cache front page post loop:', 'w3-total-cache' ),
74
+ 'description' => __( 'Caches the front page post loop, pagination is supported.', 'w3-total-cache' )
75
+ ) );
76
+ Util_Ui::config_item( array(
77
+ 'key' => array( 'genesis.theme', 'loop_terms' ),
78
+ 'control' => 'checkbox',
79
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
80
+ 'label' => __( 'Cache author/tag/categories/term post loop:', 'w3-total-cache' ),
81
+ 'description' => __( 'Caches the posts listed on tag, categories, author and other term pages, pagination is supported.', 'w3-total-cache' )
82
+ ) );
83
+ Util_Ui::config_item( array(
84
+ 'key' => array( 'genesis.theme', 'flush_terms' ),
85
+ 'control' => 'checkbox',
86
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
87
+ 'label' => __( 'Flush posts loop:', 'w3-total-cache' ),
88
+ 'description' => __( 'Flushes the posts loop cache on post updates. See setting above for affected loops.', 'w3-total-cache' )
89
+ ) );
90
+ Util_Ui::config_item( array(
91
+ 'key' => array( 'genesis.theme', 'loop_single' ),
92
+ 'control' => 'checkbox',
93
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
94
+ 'label' => __( 'Cache single post / page:', 'w3-total-cache' ),
95
+ 'description' => __( 'Caches the single post / page loop, pagination is supported.', 'w3-total-cache' )
96
+ ) );
97
+ Util_Ui::config_item( array(
98
+ 'key' => array( 'genesis.theme', 'loop_single_excluded' ),
99
+ 'control' => 'textarea',
100
+ 'label' => __( 'Excluded single pages / posts:', 'w3-total-cache' ),
101
+ 'description' => __( 'List of pages / posts that should not have the single post / post loop cached. Specify one page / post per line. This area supports regular expressions.', 'w3-total-cache' )
102
+ ) );
103
+ Util_Ui::config_item( array(
104
+ 'key' => array( 'genesis.theme', 'loop_single_genesis_comments' ),
105
+ 'control' => 'checkbox',
106
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
107
+ 'label' => __( 'Cache comments:', 'w3-total-cache' ),
108
+ 'description' => __( 'Caches the comments loop, pagination is supported.', 'w3-total-cache' )
109
+ ) );
110
+ Util_Ui::config_item( array(
111
+ 'key' => array( 'genesis.theme', 'loop_single_genesis_pings' ),
112
+ 'control' => 'checkbox',
113
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
114
+ 'label' => __( 'Cache pings:', 'w3-total-cache' ),
115
+ 'description' => __( 'Caches the ping loop, pagination is supported. One per line.', 'w3-total-cache' )
116
+ ) );
117
+ ?>
118
+ </table>
119
+ <?php Util_Ui::button_config_save( 'extension_genesis_content' ); ?>
120
+ <?php Util_Ui::postbox_footer(); ?>
121
+
122
+
123
+
124
+ <?php Util_Ui::postbox_header( __( 'Sidebar', 'w3-total-cache' ), '', 'sidebar' ); ?>
125
+ <table class="form-table">
126
+ <?php
127
+ Util_Ui::config_item( array(
128
+ 'key' => array( 'genesis.theme', 'sidebar' ),
129
+ 'control' => 'checkbox',
130
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
131
+ 'label' => __( 'Cache sidebar:', 'w3-total-cache' ),
132
+ 'description' => __( 'Caches sidebar loop, the widget area.', 'w3-total-cache' )
133
+ ) );
134
+ Util_Ui::config_item( array(
135
+ 'key' => array( 'genesis.theme', 'sidebar_excluded' ),
136
+ 'control' => 'textarea',
137
+ 'label' => __( 'Exclude pages:', 'w3-total-cache' ),
138
+ 'description' => __( 'List of pages that should not have sidebar cached. Specify one page / post per line. This area supports regular expressions.', 'w3-total-cache' )
139
+ ) );
140
+ ?>
141
+ </table>
142
+ <?php Util_Ui::button_config_save( 'extension_genesis_sidebar' ); ?>
143
+ <?php Util_Ui::postbox_footer(); ?>
144
+
145
+
146
+
147
+ <?php Util_Ui::postbox_header( __( 'Footer', 'w3-total-cache' ) ); ?>
148
+ <table class="form-table">
149
+ <?php
150
+ Util_Ui::config_item( array(
151
+ 'key' => array( 'genesis.theme', 'genesis_footer' ),
152
+ 'control' => 'checkbox',
153
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
154
+ 'label' => __( 'Cache genesis footer:', 'w3-total-cache' ),
155
+ 'description' => __( 'Caches footer loop.', 'w3-total-cache' )
156
+ ) );
157
+ Util_Ui::config_item( array(
158
+ 'key' => array( 'genesis.theme', 'wp_footer' ),
159
+ 'control' => 'checkbox',
160
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
161
+ 'label' => __( 'Cache footer:', 'w3-total-cache' ),
162
+ 'description' => __( 'Caches wp_footer loop.', 'w3-total-cache' )
163
+ ) );
164
+ Util_Ui::config_item( array(
165
+ 'key' => array( 'genesis.theme', 'reject_logged_roles' ),
166
+ 'control' => 'checkbox',
167
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
168
+ 'label' => __( 'Disable fragment cache:', 'w3-total-cache' ),
169
+ 'description' => 'Don\'t use fragment cache with the following hooks and for the specified user roles.'
170
+ ) );
171
+ ?>
172
+ </table>
173
+ <?php Util_Ui::button_config_save( 'extension_genesis_footer' ); ?>
174
+ <?php Util_Ui::postbox_footer(); ?>
175
+
176
+
177
+ <?php Util_Ui::postbox_header( __( 'Exclusions', 'w3-total-cache' ), '', 'exclusions' ); ?>
178
+ <table class="form-table">
179
+ <tr>
180
+ <td><?php _e( 'Select hooks', 'w3-total-cache' ) ?></td>
181
+ <td>
182
+ <?php
183
+
184
+ $saved_hooks = $config->get_array( array( 'genesis.theme', 'reject_logged_roles_on_actions' ) );
185
+ $name = Util_Ui::config_key_to_http_name( array( 'genesis.theme', 'reject_logged_roles_on_actions' ) );
186
+ $hooks = array(
187
+ 'genesis_header' => 'Header',
188
+ 'genesis_footer' => 'Footer',
189
+ 'genesis_sidebar' => 'Sidebar',
190
+ 'genesis_loop' =>'The Loop',
191
+ 'wp_head' => 'wp_head',
192
+ 'wp_footer' => 'wp_footer',
193
+ 'genesis_comments' => 'Comments',
194
+ 'genesis_pings' => 'Pings',
195
+ 'genesis_do_nav'=>'Primary navigation',
196
+ 'genesis_do_subnav' => 'Secondary navigation'
197
+ );
198
+ ?>
199
+
200
+ <input <?php disabled( $config->is_sealed( 'genesis.theme' ) ) ?>
201
+ type="hidden" name="<?php echo esc_attr( $name )?>" value="" />
202
+ <?php foreach ( $hooks as $hook => $hook_label ) : ?>
203
+ <input <?php disabled( $config->is_sealed( 'genesis.theme' ) ) ?>
204
+ type="checkbox" name="<?php echo esc_attr( $name )?>[]"
205
+ value="<?php echo $hook ?>"
206
+ <?php checked( in_array( $hook, $saved_hooks ) ) ?>
207
+ id="role_<?php echo $hook ?>" />
208
+ <label for="role_<?php echo $hook ?>"><?php echo $hook_label ?></label><br />
209
+ <?php endforeach; ?>
210
+
211
+ <br />
212
+ <span class="description">
213
+ <?php _e( 'Select hooks from the list that should not be cached if user belongs to any of the roles selected below.', 'w3-total-cache' ) ?>
214
+ </span>
215
+ </td>
216
+ </tr>
217
+ <tr>
218
+ <td><?php _e( 'Select roles:', 'w3-total-cache' ) ?></td>
219
+ <td>
220
+ <?php
221
+ $saved_roles = $config->get_array( array( 'genesis.theme', 'reject_roles' ) );
222
+ $name = Util_Ui::config_key_to_http_name( array( 'genesis.theme', 'reject_roles' ) );
223
+
224
+ ?>
225
+ <input type="hidden" name="<?php echo esc_attr( $name )?>" value="" />
226
+ <?php foreach ( get_editable_roles() as $role_name => $role_data ) : ?>
227
+ <input <?php disabled( $config->is_sealed( 'genesis.theme' ) ) ?>
228
+ type="checkbox"
229
+ name="<?php echo esc_attr( $name )?>[]"
230
+ value="<?php echo $role_name ?>" <?php checked( in_array( $role_name, $saved_roles ) ) ?> id="role_<?php echo $role_name ?>" />
231
+ <label for="role_<?php echo $role_name ?>"><?php echo $role_data['name'] ?></label>
232
+ <?php endforeach; ?>
233
+ <br />
234
+ <span class="description">
235
+ <?php _e( 'Select user roles that should not use the fragment cache.', 'w3-total-cache' ) ?>
236
+ </span>
237
+ </td>
238
+ </tr>
239
+ </table>
240
+ <?php Util_Ui::button_config_save( 'extension_genesis_exclusions' ); ?>
241
+ <?php Util_Ui::postbox_footer(); ?>
242
+
243
+ </div>
Extension_Genesis_Plugin.php ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_Genesis_Plugin {
5
+ /**
6
+ * Request URI
7
+ *
8
+ * @var string
9
+ */
10
+ private $_request_uri = '';
11
+ private $_config;
12
+
13
+ function __construct() {
14
+ $this->_config = Dispatcher::config();
15
+ }
16
+
17
+ function run() {
18
+ add_filter( 'w3tc_config_default_values', array(
19
+ $this, 'w3tc_config_default_values' ) );
20
+
21
+ add_action( 'w3tc_register_fragment_groups', array( $this, 'register_groups' ) );
22
+
23
+ $this->_config = Dispatcher::config();
24
+
25
+ if ( Util_Environment::is_w3tc_pro( $this->_config ) ) {
26
+ if ( !is_admin() ) {
27
+ /**
28
+ * Register the caching of content to specific hooks
29
+ */
30
+ foreach ( array( 'genesis_header', 'genesis_footer', 'genesis_sidebar', 'genesis_loop', 'wp_head', 'wp_footer', 'genesis_comments', 'genesis_pings' ) as $hook ) {
31
+ add_action( $hook, array( $this, 'cache_genesis_start' ), -999999999 );
32
+ add_action( $hook, array( $this, 'cache_genesis_end' ), 999999999 );
33
+ }
34
+ foreach ( array( 'genesis_do_subnav', 'genesis_do_nav' ) as $filter ) {
35
+ add_filter( $filter, array( $this, 'cache_genesis_filter_start' ), -999999999 );
36
+ add_filter( $filter, array( $this, 'cache_genesis_filter_end' ), 999999999 );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Since posts pages etc are cached individually need to be able to flush just those and not all fragment
42
+ */
43
+ add_action( 'clean_post_cache', array( $this, 'flush_post_fragment' ) );
44
+ add_action( 'clean_post_cache', array( $this, 'flush_terms_fragment' ), 0, 0 );
45
+
46
+ $this->_request_uri = $_SERVER['REQUEST_URI'];
47
+ }
48
+ }
49
+
50
+ public function w3tc_config_default_values( $default_values ) {
51
+ $default_values['genesis.theme'] = array(
52
+ 'wp_head' => '0',
53
+ 'genesis_header' => '1',
54
+ 'genesis_do_nav' => '0',
55
+ 'genesis_do_subnav' => '0',
56
+ 'loop_front_page' => '1',
57
+ 'loop_terms' => '1',
58
+ 'flush_terms' => '1',
59
+ 'loop_single' => '1',
60
+ 'loop_single_excluded' => '',
61
+ 'loop_single_genesis_comments' => '0',
62
+ 'loop_single_genesis_pings' => '0',
63
+ 'sidebar' => '0',
64
+ 'sidebar_excluded' => '',
65
+ 'genesis_footer' => '1',
66
+ 'wp_footer' => '0',
67
+ 'reject_logged_roles' => '1',
68
+ 'reject_logged_roles_on_actions' => array(
69
+ 'genesis_loop',
70
+ 'wp_head',
71
+ 'wp_footer',
72
+ ),
73
+ 'reject_roles' => array( 'administrator' )
74
+ );
75
+
76
+ return $default_values;
77
+ }
78
+
79
+ /**
80
+ * Start outputbuffering or return fragment on a per page/hook basis
81
+ */
82
+ function cache_genesis_start() {
83
+ $hook = current_filter();
84
+ $keys = $this->_get_id_group( $hook );
85
+ if ( is_null( $keys ) )
86
+ return;
87
+ list( $id, $group ) = $keys;
88
+ w3tc_fragmentcache_start( $id, $group, $hook );
89
+ }
90
+
91
+ /**
92
+ * Store the output buffer per page/post hook basis.
93
+ */
94
+ function cache_genesis_end() {
95
+ $keys = $this->_get_id_group( current_filter() );
96
+ if ( is_null( $keys ) )
97
+ return;
98
+ list( $id, $group ) = $keys;
99
+ w3tc_fragmentcache_end( $id, $group, $this->_config->get_boolean( array( 'fragmentcache', 'debug' ) ) );
100
+ }
101
+
102
+ /**
103
+ * Start filter buffering and return filter result
104
+ */
105
+ function cache_genesis_filter_start( $data ) {
106
+ $hook = current_filter();
107
+ $keys = $this->_get_id_group( $hook, strpos( $data, 'current' )!==false );
108
+ if ( is_null( $keys ) )
109
+ return $data;
110
+ list( $id, $group ) = $keys;
111
+ return w3tc_fragmentcache_filter_start( $id, $group, $hook, $data );
112
+ }
113
+
114
+ /**
115
+ * Store the filter result and return filter result.
116
+ */
117
+ function cache_genesis_filter_end( $data ) {
118
+ $keys = $this->_get_id_group( current_filter(), strpos( $data, 'current' )!==false );
119
+ if ( is_null( $keys ) )
120
+ return $data;
121
+ list( $id, $group ) = $keys;
122
+ return w3tc_fragmentcache_filter_end( $id, $group, $data );
123
+ }
124
+
125
+ /**
126
+ * Constructs the fragment grouping for a subgroup
127
+ *
128
+ * @param unknown $subgroup
129
+ * @param unknown $state
130
+ * @return string
131
+ */
132
+ private function _genesis_group( $subgroup, $state = false ) {
133
+ $postfix = '';
134
+ if ( $state && is_user_logged_in() )
135
+ $postfix = 'logged_in_';
136
+ return ( $subgroup ? "genesis_fragment_{$subgroup}_" : 'genesis_fragment_' ) . $postfix;
137
+ }
138
+
139
+ /**
140
+ * Constructs the correct fragment group and id for the hook
141
+ *
142
+ * @param unknown $hook
143
+ * @param bool $current_menu
144
+ * @return array|null
145
+ */
146
+ private function _get_id_group( $hook, $current_menu = false ) {
147
+ if ( $this->cannot_cache_current_hook() ) {
148
+ return null;
149
+ }
150
+ switch ( true ) {
151
+ case $keys = $this->generate_sidebar_keys():
152
+ list( $group, $genesis_id ) = $keys;
153
+ break;
154
+ case $keys = $this->generate_genesis_loop_keys():
155
+ list( $group, $genesis_id ) = $keys;
156
+ break;
157
+ case $keys = $this->generate_genesis_comments_pings_keys():
158
+ list( $group, $genesis_id ) = $keys;
159
+ break;
160
+ case $keys = $this->generate_genesis_navigation_keys( $current_menu ):
161
+ list( $group, $genesis_id ) = $keys;
162
+ break;
163
+ default:
164
+ $group = $hook;
165
+ $genesis_id = $this->get_page_slug();
166
+ if ( is_paged() )
167
+ $genesis_id .= $this->get_paged_page_key();
168
+ break;
169
+ }
170
+
171
+ if ( $this->_cache_group( $group ) && !$this->_exclude_page( $group ) ) {
172
+ return array( $genesis_id, $this->_genesis_group( $group, true ) );
173
+ }
174
+
175
+ return null;
176
+ }
177
+
178
+ /**
179
+ * Checks if the fragment group should be cached
180
+ *
181
+ * @param unknown $group
182
+ * @return array|bool|int|null|string
183
+ */
184
+ private function _cache_group( $group ) {
185
+ return $this->_config->get_string( array( 'genesis.theme', $group ) );
186
+ }
187
+
188
+ /**
189
+ * Checks if current page is excluded from caching
190
+ *
191
+ * @param unknown $group
192
+ * @return bool
193
+ */
194
+ private function _exclude_page( $group ) {
195
+ $reject_uri = $this->_config->get_array( array( 'genesis.theme', "{$group}_excluded" ) );
196
+
197
+ if ( is_null( $reject_uri ) || !is_array( $reject_uri ) || empty( $reject_uri ) ) {
198
+ return false;
199
+ }
200
+
201
+ $auto_reject_uri = array(
202
+ 'wp-login',
203
+ 'wp-register'
204
+ );
205
+ foreach ( $auto_reject_uri as $uri ) {
206
+ if ( strstr( $this->_request_uri, $uri ) !== false ) {
207
+ return true;
208
+ }
209
+ }
210
+
211
+ $reject_uri = array_map( array( '\W3TC\Util_Environment', 'parse_path' ), $reject_uri );
212
+
213
+ foreach ( $reject_uri as $expr ) {
214
+ $expr = trim( $expr );
215
+ if ( $expr != '' && preg_match( '~' . $expr . '~i', $this->_request_uri ) ) {
216
+ return true;
217
+ }
218
+ }
219
+
220
+ return false;
221
+ }
222
+
223
+ /**
224
+ * Register the various fragments groups to be used. no_action is used since fragments requires actions.
225
+ */
226
+ function register_groups() {
227
+ //blog specific group and an array of actions that will trigger a flush of the group
228
+ $groups = array (
229
+ $this->_genesis_group( '' ) => array(
230
+ 'clean_post_cache',
231
+ 'update_option_sidebars_widgets',
232
+ 'wp_update_nav_menu_item' ),
233
+ $this->_genesis_group( 'sidebar' ) => array(
234
+ 'update_option_sidebars_widgets' ),
235
+ $this->_genesis_group( 'loop_single' ) => array(
236
+ 'no_action' ),
237
+ $this->_genesis_group( 'loop_front_page' ) => array(
238
+ 'clean_post_cache' ),
239
+ $this->_genesis_group( 'loop_terms' ) => array(
240
+ 'no_action' )
241
+ );
242
+ foreach ( $groups as $group => $actions )
243
+ w3tc_register_fragment_group( $group, $actions, 3600 );
244
+ }
245
+
246
+ /**
247
+ * Flush the fragments connected to a post id
248
+ *
249
+ * @param unknown $post_ID
250
+ */
251
+ function flush_post_fragment( $post_ID ) {
252
+ $page_slug = $this->get_page_slug( $post_ID );
253
+ $urls = Util_PageUrls::get_post_urls( $post_ID );
254
+ $hooks = array( 'genesis_loop', 'genesis_comments', 'genesis_pings' );
255
+ foreach ( $hooks as $hook ) {
256
+ $genesis_id = $page_slug;
257
+ $genesis_id = "{$hook}_{$genesis_id}";
258
+
259
+ w3tc_fragmentcache_flush_fragment( $genesis_id, $this->_genesis_group( 'loop_single_logged_in' ) );
260
+ w3tc_fragmentcache_flush_fragment( $genesis_id, $this->_genesis_group( 'loop_single' ) );
261
+ for ( $page = 0; $page<=sizeof( $urls ); $page++ ) {
262
+ $genesis_id = $page_slug;
263
+ $genesis_id .= $this->get_paged_page_key( $page );
264
+ $genesis_id = "{$hook}_{$genesis_id}";
265
+
266
+ w3tc_fragmentcache_flush_fragment( $genesis_id, $this->_genesis_group( 'loop_single_logged_in' ) );
267
+ w3tc_fragmentcache_flush_fragment( $genesis_id, $this->_genesis_group( 'loop_single' ) );
268
+ }
269
+ }
270
+ }
271
+
272
+ public function flush_terms_fragment() {
273
+ if ( $this->_config->get_boolean( array( 'genesis.theme', 'flush_terms' ) ) ) {
274
+ w3tc_fragmentcache_flush_group( 'loop_terms' );
275
+ }
276
+ }
277
+
278
+ /**
279
+ *
280
+ *
281
+ * @return bool
282
+ */
283
+ private function cannot_cache_current_hook() {
284
+ if ( is_user_logged_in() && $this->_config->get_boolean( array( 'genesis.theme', 'reject_logged_roles' ) ) ) {
285
+ $roles = $this->_config->get_array( array( 'genesis.theme', 'reject_roles' ) );
286
+ if ( $roles ) {
287
+ $hooks = $this->_config->get_array( array( 'genesis.theme', 'reject_logged_roles_on_actions' ) );
288
+ $hook = current_filter();
289
+ foreach ( $roles as $role ) {
290
+ if ( $hooks && current_user_can( $role ) && in_array( $hook, $hooks ) ) {
291
+ return true;
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return false;
297
+ }
298
+
299
+ /**
300
+ *
301
+ *
302
+ * @return array
303
+ */
304
+ private function generate_genesis_loop_keys() {
305
+ if ( ( $hook = current_filter() ) != 'genesis_loop' )
306
+ return null;
307
+
308
+ if ( is_front_page() ) {
309
+ $group = 'loop_front_page';
310
+ } elseif ( is_single() ) {
311
+ $group = 'loop_single';
312
+ } else {
313
+ $group = 'loop_terms';
314
+ }
315
+ $genesis_id = $this->get_page_slug();
316
+ if ( is_paged() )
317
+ $genesis_id .= $this->get_paged_page_key();
318
+ $genesis_id = "{$hook}_{$genesis_id}";
319
+
320
+ return array( $group, $genesis_id );
321
+ }
322
+
323
+ /**
324
+ *
325
+ *
326
+ * @return array
327
+ */
328
+ private function generate_sidebar_keys() {
329
+ if ( strpos( $hook = current_filter(), 'sidebar' ) !== true )
330
+ return null;
331
+
332
+ $genesis_id = $hook;
333
+ $group = 'sidebar';
334
+ return array( $group, $genesis_id );
335
+ }
336
+
337
+ /**
338
+ *
339
+ *
340
+ * @return array|null
341
+ */
342
+ private function generate_genesis_comments_pings_keys() {
343
+ if ( ( $hook = current_filter() ) != 'genesis_comments' )
344
+ return null;
345
+ $group = 'loop_single';
346
+
347
+ $genesis_id = $this->get_page_slug();
348
+ if ( is_paged() )
349
+ $genesis_id .= $this->get_paged_page_key();
350
+ $genesis_id = "{$hook}_{$genesis_id}";
351
+
352
+ return array( $group, $genesis_id );
353
+ }
354
+
355
+ /**
356
+ *
357
+ *
358
+ * @param string $current_menu
359
+ * @return array|null
360
+ */
361
+ private function generate_genesis_navigation_keys( $current_menu ) {
362
+ if ( !( strpos( ( $hook = current_filter() ), '_nav' ) && $current_menu ) )
363
+ return null;
364
+
365
+ $genesis_id = $this->get_page_slug();
366
+ if ( is_paged() )
367
+ $genesis_id .= $this->get_paged_page_key();
368
+ return array( $hook, $genesis_id );
369
+ }
370
+
371
+ private function get_page_slug( $post_ID = null ) {
372
+ if ( $post_ID ) {
373
+ $purl = get_permalink( $post_ID );
374
+ return str_replace( '/', '-', trim( str_replace( home_url(), '', $purl ), "/" ) );
375
+ }
376
+ if ( is_front_page() )
377
+ return 'front_page';
378
+ return str_replace( '/', '-', trim( $_SERVER['REQUEST_URI'], "/" ) );
379
+ }
380
+
381
+ /**
382
+ *
383
+ *
384
+ * @param int|null $page
385
+ * @return string _pagenumber_
386
+ */
387
+ private function get_paged_page_key( $page=null ) {
388
+ if ( is_null( $page ) ) {
389
+ global $wp_query;
390
+ return '_' . $wp_query->query_vars['paged'] . '_';
391
+ }
392
+
393
+ return '_' . $page . '_';
394
+ }
395
+ }
396
+
397
+ $p = new Extension_Genesis_Plugin();
398
+ $p->run();
399
+
400
+ if ( is_admin() ) {
401
+ $p = new Extension_Genesis_Plugin_Admin();
402
+ $p->run();
403
+ }
Extension_Genesis_Plugin_Admin.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+ /**
4
+ * W3 GenesisExtension module
5
+ */
6
+
7
+ class Extension_Genesis_Plugin_Admin {
8
+ function run() {
9
+ add_action( 'w3tc_extension_page_genesis.theme', array(
10
+ '\W3TC\Extension_Genesis_Page',
11
+ 'w3tc_extension_page_genesis_theme'
12
+ ) );
13
+ add_filter( 'w3tc_errors', array( $this, 'w3tc_errors' ) );
14
+ }
15
+
16
+
17
+
18
+ /**
19
+ *
20
+ *
21
+ * @param unknown $extensions
22
+ * @param Config $config
23
+ * @return mixed
24
+ */
25
+ static public function w3tc_extensions( $extensions, $config ) {
26
+ $requirements = array();
27
+
28
+ if ( !self::is_theme_found() )
29
+ $requirements[] =
30
+ 'Optimizes "Genesis Framework" version >= 1.9.0, which is not active';
31
+
32
+ if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) )
33
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
34
+
35
+ if ( !$config->is_extension_active( 'fragmentcache' ) )
36
+ $requirements[] = 'Activate "Fragment Cache" extension first';
37
+
38
+ $extensions['genesis.theme'] = array (
39
+ 'name' => 'Genesis Framework by StudioPress',
40
+ 'author' => 'W3 EDGE',
41
+ 'description' => 'Provides 30-60% improvement in page generation time for the Genesis Framework by Copyblogger Media.',
42
+ 'author_uri' => 'https://www.w3-edge.com/',
43
+ 'extension_uri' => 'https://www.w3-edge.com/',
44
+ 'extension_id' => 'genesis.theme',
45
+ 'version' => '0.1',
46
+ 'enabled' => empty( $requirements ),
47
+ 'requirements' => implode( ', ', $requirements ),
48
+ 'path' => 'w3-total-cache/Extension_Genesis_Plugin.php'
49
+ );
50
+
51
+ return $extensions;
52
+ }
53
+
54
+
55
+
56
+ /**
57
+ * Show errors in wp-admin
58
+ */
59
+ function w3tc_errors( $errors ) {
60
+ $c = Dispatcher::config();
61
+
62
+ if ( !$c->is_extension_active_frontend( 'fragmentcache' ) ) {
63
+ $errors['genesis_fragmentcache_disabled'] =
64
+ __( 'Please enable <strong>Fragment Cache</strong> module to make sure <strong>Genesis extension</strong> works properly.',
65
+ 'w3-total-cache' );
66
+ }
67
+
68
+ return $errors;
69
+ }
70
+
71
+
72
+
73
+ static private function is_theme_found() {
74
+ if ( !is_network_admin() )
75
+ return ( defined( 'PARENT_THEME_NAME' ) &&
76
+ PARENT_THEME_NAME == 'Genesis' );
77
+
78
+ $themes = Util_Theme::get_themes();
79
+ $theme_found = false;
80
+ foreach ( $themes as $theme ) {
81
+ if ( strtolower( $theme->Template ) == 'genesis' )
82
+ return true;
83
+ }
84
+ }
85
+
86
+
87
+
88
+ /**
89
+ * called from outside, since can show notice even when extension is not active
90
+ */
91
+ static public function w3tc_extensions_hooks( $hooks ) {
92
+ if ( !self::show_notice() )
93
+ return $hooks;
94
+
95
+ if ( !isset( $hooks['filters']['w3tc_notes'] ) )
96
+ $hooks['filters']['w3tc_notes'] = array();
97
+
98
+ $hooks['filters']['w3tc_notes'][] = 'w3tc_notes_genesis_theme';
99
+ return $hooks;
100
+ }
101
+
102
+ static private function show_notice() {
103
+ $config = Dispatcher::config();
104
+ if ( $config->is_extension_active( 'genesis.theme' ) )
105
+ return false;
106
+
107
+ if ( !self::is_theme_found() )
108
+ return false;
109
+
110
+ $state = Dispatcher::config_state();
111
+ if ( $state->get_boolean( 'genesis.theme.hide_note_suggest_activation' ) )
112
+ return false;
113
+
114
+ return true;
115
+ }
116
+
117
+ static public function w3tc_notes_genesis_theme( $notes ) {
118
+ if ( !self::show_notice() )
119
+ return $notes;
120
+
121
+ $extension_id = 'genesis.theme';
122
+
123
+ $notes[$extension_id] = sprintf(
124
+ __( 'It appears that activating the <a href="%s">Genesis Theme</a> extension for W3 Total Cache will be helpful for your site. <a href="%s">Click here</a> to try it. %s',
125
+ 'w3-total-cache' ),
126
+ Util_Ui::admin_url( 'admin.php?page=w3tc_extensions#' . $extension_id ),
127
+ Util_Ui::url( array( 'w3tc_extensions_activate' => $extension_id ) ),
128
+ Util_Ui::button_link(
129
+ __( 'Hide this message', 'w3-total-cache' ),
130
+ Util_Ui::url( array(
131
+ 'w3tc_default_config_state' => 'y',
132
+ 'key' => 'genesis.theme.hide_note_suggest_activation',
133
+ 'value' => 'true' ) ) ) );
134
+
135
+ return $notes;
136
+ }
137
+ }
Extension_NewRelic_AdminActions.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_AdminActions {
7
+ private $_config = null;
8
+
9
+ function __construct() {
10
+ $this->_config = Dispatcher::config();
11
+ }
12
+
13
+
14
+
15
+ function w3tc_save_new_relic() {
16
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
17
+ $application = Util_Request::get_array( 'application' );
18
+ $application['alerts_enabled'] = $application['alerts_enabled'] == 1 ? 'true' : 'false';
19
+ $application['rum_enabled'] = $application['rum_enabled'] == 1 ? 'true' : 'false';
20
+ $result=$service->update_application_settings( $application );
21
+ Util_Admin::redirect( array(
22
+ 'w3tc_note' => 'new_relic_save'
23
+ ), true );
24
+ }
25
+ }
Extension_NewRelic_AdminNotes.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_NewRelic_AdminNotes {
5
+ /**
6
+ *
7
+ *
8
+ * @param Config $config
9
+ * @return string
10
+ */
11
+ function notifications( $config ) {
12
+ $config_state = Dispatcher::config_state();
13
+ if ( !$config_state->get_boolean( 'newrelic.hide_note_pageload_slow' ) ) {
14
+ $pl = get_option( 'w3tc_nr_frontend_response_time' );
15
+
16
+ if ( $pl !== false && $pl>0.3 ) {
17
+ $nr_recommends = array();
18
+ if ( !$config->get_boolean( 'pgcache.enabled' ) )
19
+ $nr_recommends[] = __( 'Page Cache', 'w3-total-cache' );
20
+ if ( !$config->get_boolean( 'minify.enabled' ) )
21
+ $nr_recommends[] = __( 'Minify', 'w3-total-cache' );
22
+ if ( !$config->get_boolean( 'cdn.enabled' ) )
23
+ $nr_recommends[] = __( 'CDN', 'w3-total-cache' );
24
+ if ( !$config->get_boolean( 'browsercache.enabled' ) )
25
+ $nr_recommends[] = __( 'Browser Cache and use compression', 'w3-total-cache' );
26
+
27
+ if ( $nr_recommends ) {
28
+ $message = sprintf(
29
+ __( 'Application monitoring has detected that your page load time is higher than 300ms. It is recommended that you enable the following features: %s %s',
30
+ 'w3-total-cache' ),
31
+ implode( ', ', $nr_recommends ),
32
+ Util_Ui::button_link( 'Hide this message',
33
+ Util_Ui::url( array(
34
+ 'w3tc_default_config_state' => 'y',
35
+ 'key' => 'newrelic.hide_note_pageload_slow',
36
+ 'value' => 'true' ) ) ) );
37
+ return array(
38
+ 'newrelic_recommends' => $message
39
+ );
40
+ }
41
+ }
42
+ }
43
+
44
+ return array();
45
+ }
46
+ }
Extension_NewRelic_Api.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Interacts with the New Relic Connect API
6
+ *
7
+ * @link newrelic.github.com/newrelic_api/
8
+ */
9
+ class Extension_NewRelic_Api {
10
+ private $_api_key;
11
+ static private $url = 'https://api.newrelic.com';
12
+
13
+ /**
14
+ *
15
+ *
16
+ * @param string $api_key New Relic API Key
17
+ */
18
+ function __construct( $api_key ) {
19
+ $this->_api_key = $api_key;
20
+ }
21
+
22
+ /**
23
+ *
24
+ *
25
+ * @param string $api_call_url url path with query string used to define what to get from the NR API
26
+ * @throws \Exception
27
+ * @return bool
28
+ */
29
+ private function _get( $api_call_url, $query = array() ) {
30
+ $defaults = array(
31
+ 'headers'=>'x-api-key:'.$this->_api_key,
32
+ 'body' => $query
33
+ );
34
+ $url = self::$url . $api_call_url;
35
+
36
+ $response = wp_remote_get( $url, $defaults );
37
+
38
+ if ( is_wp_error( $response ) ) {
39
+ throw new \Exception( 'Could not get data' );
40
+ } elseif ( $response['response']['code'] == 200 ) {
41
+ $return = $response['body'];
42
+ } else {
43
+ switch ( $response['response']['code'] ) {
44
+ case '403':
45
+ $message = __( 'Invalid API key', 'w3-total-cache' );
46
+ break;
47
+ default:
48
+ $message = $response['response']['message'];
49
+ }
50
+
51
+ throw new \Exception( $message, $response['response']['code'] );
52
+ }
53
+ return $return;
54
+ }
55
+
56
+ /**
57
+ *
58
+ *
59
+ * @param string $api_call_url url path with query string used to define what to get from the NR API
60
+ * @param array $params key value array.
61
+ * @throws \Exception
62
+ * @return bool
63
+ */
64
+ private function _put( $api_call_url, $params ) {
65
+ $defaults = array(
66
+ 'method' => 'PUT',
67
+ 'headers'=>'x-api-key:'.$this->_api_key,
68
+ 'body' => $params
69
+ );
70
+ $url = self::$url . $api_call_url;
71
+ $response = wp_remote_request( $url, $defaults );
72
+
73
+ if ( is_wp_error( $response ) ) {
74
+ throw new \Exception( 'Could not put data' );
75
+ } elseif ( $response['response']['code'] == 200 ) {
76
+ $return = true;
77
+ } else {
78
+ throw new \Exception( $response['response']['message'], $response['response']['code'] );
79
+ }
80
+ return $return;
81
+ }
82
+
83
+
84
+
85
+ function get_browser_applications() {
86
+ $response = $this->_get( '/v2/browser_applications.json' );
87
+ $r = @json_decode( $response, true );
88
+ if ( !$r )
89
+ throw new \Exception( 'Received unexpected response' );
90
+
91
+ if ( !isset( $r['browser_applications'] ) )
92
+ return array();
93
+
94
+ return $r['browser_applications'];
95
+ }
96
+
97
+
98
+
99
+ function get_browser_application( $id ) {
100
+ $response = $this->_get( '/v2/browser_applications.json', array(
101
+ 'filter[ids]' => $id ) );
102
+ $r = @json_decode( $response, true );
103
+ if ( !$r )
104
+ throw new \Exception( 'Received unexpected response' );
105
+
106
+ if ( !isset( $r['browser_applications'] ) ||
107
+ count( $r['browser_applications'] ) != 1 )
108
+ return null;
109
+
110
+ return $r['browser_applications'][0];
111
+ }
112
+ }
Extension_NewRelic_Core.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_NewRelic_Core {
5
+ public function get_effective_browser_application() {
6
+ $c = Dispatcher::config();
7
+ $api_key = $c->get( array( 'newrelic', 'api_key' ) );
8
+ $id = $c->get( array( 'newrelic', 'browser.application_id' ) );
9
+
10
+ if ( empty( $api_key ) || empty( $id ) )
11
+ return null;
12
+
13
+ $applications_string = get_option( 'w3tc_nr_browser_applications' );
14
+ $applications = @json_decode( $applications_string, true );
15
+ if ( !is_array( $applications ) )
16
+ $applications = array();
17
+
18
+ if ( isset( $applications[$id] ) )
19
+ return $applications[$id];
20
+
21
+ try {
22
+ $api = new Extension_NewRelic_Api( $api_key );
23
+ $app = $api->get_browser_application( $id );
24
+
25
+ if ( !is_null( $app ) ) {
26
+ $applications[$id] = $app;
27
+ update_option( 'w3tc_nr_browser_applications',
28
+ json_encode( $applications ) );
29
+ }
30
+
31
+ return $app;
32
+ } catch ( \Exception $ex ) {
33
+ return null;
34
+ }
35
+ }
36
+ }
Extension_NewRelic_GeneralPage.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_GeneralPage {
7
+ /**
8
+ * W3TC General settings page modifications
9
+ */
10
+ static public function admin_init_w3tc_general() {
11
+ $o = new Extension_NewRelic_GeneralPage();
12
+
13
+ add_filter( 'w3tc_settings_general_anchors',
14
+ array( $o, 'w3tc_settings_general_anchors' ) );
15
+ add_action( 'w3tc_settings_general_boxarea_monitoring',
16
+ array( $o, 'w3tc_settings_general_boxarea_monitoring' ) );
17
+
18
+ wp_enqueue_script( 'w3tc_extension_newrelic_popup',
19
+ plugins_url( 'Extension_NewRelic_Popup_View.js', W3TC_FILE ),
20
+ array( 'jquery' ), '1.0' );
21
+ }
22
+
23
+
24
+
25
+
26
+ public function w3tc_settings_general_anchors( $anchors ) {
27
+ $anchors[] = array( 'id' => 'monitoring', 'text' => 'Monitoring' );
28
+ return $anchors;
29
+ }
30
+
31
+
32
+
33
+ public function w3tc_settings_general_boxarea_monitoring() {
34
+ $config = Dispatcher::config();
35
+
36
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
37
+ $new_relic_installed = $nerser->module_is_enabled();
38
+ $effective_appname = $nerser->get_effective_appname();
39
+
40
+ include W3TC_DIR . '/Extension_NewRelic_GeneralPage_View.php';
41
+ }
42
+ }
Extension_NewRelic_GeneralPage_View.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php
9
+ Util_Ui::postbox_header( __( 'Monitoring', 'w3-total-cache' ), '', 'monitoring' );
10
+ Util_Ui::config_overloading_button( array(
11
+ 'key' => 'newrelic.configuration_overloaded'
12
+ ) );
13
+ ?>
14
+
15
+ <?php if ( !$new_relic_installed ): ?>
16
+ <p><?php echo sprintf( __( '
17
+ New Relic may not be installed or not active on this server. %s. Visit %s for installation instructions.', 'w3-total-cache' )
18
+ , '<a href="' . esc_attr( NEWRELIC_SIGNUP_URL ) . '" target="_blank">' . __( 'Sign up for a (free) account', 'w3-total-cache' ) . '</a>'
19
+ , '<a href="https://newrelic.com/docs/php/new-relic-for-php" target="_blank">New Relic</a>' )
20
+ ?>
21
+ </p>
22
+ <?php endif; ?>
23
+
24
+ <table class="form-table">
25
+ <tr>
26
+ <th>
27
+ <label for="newrelic_api_key"><?php
28
+ _e( '<acronym title="Application Programming Interface">API</acronym> key:', 'w3-total-cache' )
29
+ ?></label>
30
+ </th>
31
+ <td class="w3tc-td-with-button">
32
+ <?php echo htmlspecialchars( $config->get_string( array( 'newrelic', 'api_key' ) ) ) ?>
33
+ <input type="button" class="button w3tcnr_configure" value="Configure"
34
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
35
+ </td>
36
+ </tr>
37
+ <tr>
38
+ <th>
39
+ <label><?php _e( 'Application name:' , 'w3-total-cache' ) ?></label>
40
+ </th>
41
+ <td class="w3tc-td-with-button"><?php
42
+ if ( $config->get_string( array( 'newrelic', 'monitoring_type' ) ) == 'browser' )
43
+ echo '(browser) ';
44
+
45
+ echo htmlspecialchars( $effective_appname );
46
+ ?></td>
47
+ </tr>
48
+ </table>
49
+ <?php Util_Ui::button_config_save( 'general_newrelic' ); ?>
50
+ <?php Util_Ui::postbox_footer(); ?>
Extension_NewRelic_Page.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_Page extends Base_Page_Settings {
7
+ /**
8
+ * Current page
9
+ *
10
+ * @var string
11
+ */
12
+ protected $_page = 'w3tc_monitoring';
13
+
14
+
15
+
16
+ public function render_content() {
17
+ $config = Dispatcher::config();
18
+ $monitoring_type = $config->get_string( array( 'newrelic', 'monitoring_type' ) );
19
+ if ( $monitoring_type == 'browser' ) {
20
+ return;
21
+ }
22
+
23
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
24
+ $new_relic_configured = $config->get_string( array( 'newrelic', 'api_key' ) ) &&
25
+ $config->get_string( array( 'newrelic', 'apm.application_name' ) );
26
+ $verify_running = $nerser->verify_running();
27
+ $application_settings = array();
28
+
29
+ try {
30
+ $application_settings = $nerser->get_application_settings();
31
+ } catch ( \Exception $ex ) {
32
+ $application_settings = array();
33
+ }
34
+
35
+ if ( $view_metric = Util_Request::get_boolean( 'view_metric', false ) ) {
36
+ $metric_names = $nerser->get_metric_names( Util_Request::get_string( 'regex', '' ) );
37
+ }
38
+
39
+ include W3TC_DIR . '/Extension_NewRelic_Page_View_Apm.php';
40
+ }
41
+ }
Extension_NewRelic_Page_View_Apm.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <p>
9
+ Jump to:
10
+ <a href="admin.php?page=w3tc_general"><?php _e( 'Main Menu', 'w3-total-cache' ); ?></a> |
11
+ <a href="admin.php?page=w3tc_extensions"><?php _e( 'Extensions', 'w3-total-cache' ); ?></a>
12
+ </p>
13
+ <p>
14
+ NewRelic extension is currently <?php
15
+ if ( $config->is_extension_active_frontend( 'newrelic' ) )
16
+ echo '<span class="w3tc-enabled">enabled</span>';
17
+ else
18
+ echo '<span class="w3tc-disabled">disabled</span>';
19
+ ?>.
20
+ <p>
21
+
22
+ <form action="admin.php?page=w3tc_monitoring" method="post">
23
+ <div class="metabox-holder">
24
+ <?php Util_Ui::postbox_header( __( 'Application Settings', 'w3-total-cache' ), '', 'application' ); ?>
25
+ <?php if ( $application_settings ): ?>
26
+ <table class="form-table">
27
+ <tr>
28
+ <th>
29
+ <label>Application ID:</label>
30
+ </th>
31
+ <td>
32
+ <?php esc_attr_e( $application_settings['application-id'] )?>
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <th>
37
+ <label>Application name:</label>
38
+ </th>
39
+ <td>
40
+ <?php esc_attr_e( $application_settings['name'] )?>
41
+ </td>
42
+ </tr>
43
+ <tr>
44
+ <th>
45
+ <label for="alerts-enabled">Alerts enabled:</label>
46
+ </th>
47
+ <td>
48
+ <input name="alerts-enabled]" type="hidden" value="false" />
49
+ <input id="alerts-enabled" name="application[alerts_enabled]"
50
+ type="checkbox" value="1" <?php checked( $application_settings['alerts-enabled'], 'true' ) ?> <?php Util_Ui::sealing_disabled( 'newrelic' ) ?>/>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <th>
55
+ <label for="app-apdex-t">Application ApDex Threshold:</label>
56
+ </th>
57
+ <td>
58
+ <input id="app-apdex-t" name="application[app_apdex_t]" type="text"
59
+ value="<?php echo esc_attr( $application_settings['app-apdex-t'] )?>"
60
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <th>
65
+ <label for="rum-apdex-t"><acronym title="Real User Monitoring">RUM</acronym> ApDex Threshold:</label>
66
+ </th>
67
+ <td>
68
+ <input id="rum-apdex-t" name="application[rum_apdex_t]" type="text"
69
+ value="<?php echo esc_attr( $application_settings['rum-apdex-t'] )?>"
70
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?>/>
71
+ </td>
72
+ </tr>
73
+ <tr>
74
+ <th>
75
+ <label for="rum-enabled"><acronym title="Real User Monitoring">RUM</acronym> enabled:</label>
76
+ </th>
77
+ <td>
78
+ <input name="application[rum_enabled]" type="hidden" value="false"
79
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
80
+ <input id="rum-enabled" name="application[rum_enabled]"
81
+ type="checkbox" value="1"
82
+ <?php checked( $application_settings['rum-enabled'], 'true' ) ?>
83
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?>/>
84
+ </td>
85
+ </tr>
86
+ </table>
87
+ <p class="submit">
88
+ <?php echo Util_Ui::nonce_field( 'w3tc' ); ?>
89
+ <input type="submit" name="w3tc_save_new_relic"
90
+ class="w3tc-button-save button-primary"
91
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?>
92
+ value="Save New Relic settings" />
93
+ </p>
94
+ <?php elseif ( empty( $application_settings ) ): ?>
95
+ <p><span class="description"><?php echo sprintf( __( 'Application settings could not be retrieved. New Relic may not be properly configured, <a href="%s">review the settings</a>.', 'w3-total-cache' ), network_admin_url( 'admin.php?page=w3tc_general#monitoring' ) ) ?></span></p>
96
+ <?php else: ?>
97
+ <p><?php _e( 'Application settings are only visible when New Relic is enabled', 'w3-total-cache', 'w3-total-cache' ) ?></p>
98
+ <?php endif; ?>
99
+ <?php Util_Ui::postbox_footer(); ?>
100
+ </form>
101
+ <form action="admin.php?page=w3tc_monitoring" method="post">
102
+
103
+ <?php Util_Ui::postbox_header( __( 'Dashboard Settings', 'w3-total-cache' ), '', 'dashboard' ); ?>
104
+ <table class="form-table">
105
+ <tr>
106
+ <th>
107
+ <label for="newrelic_cache_time"><?php
108
+ _e( 'Cache time:', 'w3-total-cache' )
109
+ ?></label></th>
110
+ <td><input id="newrelic_cache_time" name="extension__newrelic__cache_time"
111
+ type="text" value="<?php echo esc_attr( $config->get_integer( array( 'newrelic', 'cache_time', 5 ) ) ) ?>"
112
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
113
+ <p><span class="description">
114
+ <?php _e( 'How many minutes data retrieved from New Relic should be stored. Minimum is 1 minute.', 'w3-total-cache' ) ?>
115
+ </span>
116
+ </p>
117
+ </td>
118
+ </tr>
119
+ </table>
120
+ <?php Util_Ui::button_config_save( 'extension_newrelic_dashboard' ); ?>
121
+ <?php Util_Ui::postbox_footer(); ?>
122
+
123
+ <?php Util_Ui::postbox_header( __( 'Behavior Settings', 'w3-total-cache' ), '', 'behavior' ); ?>
124
+ <table class="form-table">
125
+ <tr>
126
+ <th colspan="2">
127
+ <?php
128
+ Util_Ui::checkbox( '',
129
+ Util_Ui::config_key_to_http_name( array( 'newrelic', 'accept.logged_roles' ) ),
130
+ $config->get_boolean( array( 'newrelic', 'accept.logged_roles' ) ),
131
+ $config->is_sealed( 'newrelic' ) );
132
+ _e( 'Use <acronym title="Real User Monitoring">RUM</acronym> only for following user roles', 'w3-total-cache' )
133
+ ?></label><br />
134
+ <span class="description"><?php
135
+ _e( 'Select user roles that <acronym title="Real User Monitoring">RUM</acronym> should be enabled for:', 'w3-total-cache' )
136
+ ?></span>
137
+
138
+ <div id="newrelic_accept_roles">
139
+ <?php $saved_roles = $config->get_array( array( 'newrelic', 'accept.roles' ) ); ?>
140
+ <input type="hidden" name="newrelic___accept__roles" value="" /><br />
141
+ <?php foreach ( get_editable_roles() as $role_name => $role_data ) : ?>
142
+ <input type="checkbox" name="newrelic___accept__roles[]" value="<?php echo $role_name ?>"
143
+ <?php checked( in_array( $role_name, $saved_roles ) ) ?>
144
+ id="role_<?php echo $role_name ?>"
145
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
146
+ <label for="role_<?php echo $role_name ?>"><?php echo $role_data['name'] ?></label>
147
+ <?php endforeach; ?>
148
+ </div>
149
+ </th>
150
+ </tr>
151
+ <tr>
152
+ <th>
153
+ <label for="newrelic_include_rum"><?php
154
+ _e( 'Include <acronym title="Real User Monitoring">RUM</acronym> in compressed or cached pages', 'w3-total-cache' )
155
+ ?></label>
156
+ </th>
157
+ <td>
158
+ <input name="extension__newrelic__include_rum" type="hidden" value="0"
159
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
160
+ <input id="newrelic_include_rum" name="extension__newrelic__include_rum"
161
+ type="checkbox" value="1"
162
+ <?php checked( $config->get_boolean( array( 'newrelic', 'include_rum' ) ) ) ?>
163
+ <?php Util_Ui::sealing_disabled( 'newrelic' ) ?> />
164
+ <p><span class="description">
165
+ <?php _e( 'This enables inclusion of <acronym title="Real User Monitoring">RUM</acronym> when using Page Cache together with Browser Cache gzip or when using Page Cache with Disc: Enhanced', 'w3-total-cache' )?>
166
+ </span>
167
+ </p>
168
+ </td>
169
+ </tr>
170
+ <tr>
171
+ <th>
172
+ <label for="newrelic_use_php_function"><?php
173
+ _e( 'Use PHP function to set application name:', 'w3-total-cache' )
174
+ ?></label></th>
175
+ <td>
176
+ <?php if ( Util_Environment::is_wpmu() ): ?>
177
+ <input id="newrelic_use_php_function" name="extension__newrelic__use_php_function" type="checkbox" value="1" checked="checked" disabled="disabled" />
178
+ <p><span class="description">
179
+ <?php _e( 'This is required when using New Relic on a network install to set the proper names for sites.', 'w3-total-cache' ) ?></span></p>
180
+ <?php else: ?>
181
+ <input name="extension__newrelic__use_php_function" type="hidden" value="0" />
182
+ <input id="newrelic_use_php_function" name="extension__newrelic__use_php_function" type="checkbox" value="1" <?php checked( $config->get_boolean( array( 'newrelic', 'use_php_function' ) ) ) ?>/>
183
+ <p><span class="description">
184
+ <?php _e( 'Enable this to dynamically set proper application name. (See New Relic <a href="https://newrelic.com/docs/php/per-directory-settings">Per-directory settings</a> for other methods.', 'w3-total-cache' ) ?></span>
185
+ </p>
186
+ <?php endif ?>
187
+ </td>
188
+ </tr>
189
+ <tr>
190
+ <th>
191
+ <label for="newrelic_enable_xmit"><?php
192
+ _e( 'Enable XMIT', 'w3-total-cache' )
193
+ ?></label>
194
+ </th>
195
+ <td><input name="" type="hidden" value="0" />
196
+ <input id="newrelic_enable_xmit" name="extension__newrelic__enable_xmit" type="checkbox" value="1" <?php checked( $config->get_boolean( array( 'newrelic', 'enable_xmit' ) ) ) ?> <?php Util_Ui::sealing_disabled( 'newrelic' ) ?>/>
197
+ <p><span class="description"><?php _e( sprintf( 'Enable this if you want to record the metric and transaction data (until the name is changed using PHP function), specify a value of true for this argument to make the agent send the transaction to the daemon. There is a slight performance impact as it takes a few milliseconds for the agent to dump its data. <em>From %s</em>',
198
+ '<a href="https://newrelic.com/docs/php/the-php-api">New Relic PHP API doc</a>' )
199
+ , 'w3-total-cache' )?></span></p>
200
+ </td>
201
+ </tr>
202
+ </table>
203
+ <?php Util_Ui::button_config_save( 'extension_newrelic_behaviour' ); ?>
204
+ <?php Util_Ui::postbox_footer(); ?>
205
+ </form>
206
+ </div>
207
+ <?php if ( $view_metric ):?>
208
+ <table>
209
+ <?php foreach ( $metric_names as $metric ):?>
210
+ <tr>
211
+ <th style="text-align: right"><strong><?php echo $metric->name ?></strong></th>
212
+ <td><?php echo implode( ', ', $metric->fields ) ?></td>
213
+ </tr>
214
+ <?php endforeach; ?>
215
+ </table>
216
+ <?php endif; ?>
Extension_NewRelic_Plugin.php ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_Plugin {
7
+
8
+ /**
9
+ * New Relic reject reason
10
+ *
11
+ * @var string
12
+ */
13
+ var $newrelic_reject_reason = '';
14
+
15
+ /**
16
+ * Config
17
+ */
18
+ private $_config = null;
19
+
20
+ function __construct() {
21
+ $this->_config = Dispatcher::config();
22
+ }
23
+
24
+ /**
25
+ * Runs plugin
26
+ */
27
+ function run() {
28
+ add_filter( 'w3tc_config_default_values', array(
29
+ $this, 'w3tc_config_default_values' ) );
30
+
31
+ $config = Dispatcher::config();
32
+ // remainder only when extension is frontend-active
33
+ if ( !$config->is_extension_active_frontend( 'newrelic' ) )
34
+ return;
35
+
36
+ if ( $this->_config->get_string( array( 'newrelic', 'monitoring_type' ) ) == 'browser' ) {
37
+ Util_Bus::add_ob_callback( 'newrelic', array(
38
+ $this, 'ob_callback_browser' ) );
39
+ } else {
40
+ require_once W3TC_LIB_NEWRELIC_DIR . '/NewRelicWrapper.php';
41
+
42
+ $this->set_appname();
43
+
44
+ if ( defined( 'DOING_CRON' ) && DOING_CRON )
45
+ $this->background_task();
46
+
47
+ Util_Bus::add_ob_callback( 'newrelic', array(
48
+ $this, 'ob_callback_apm' ) );
49
+ }
50
+
51
+ add_filter( 'w3tc_footer_comment', array(
52
+ $this,
53
+ 'w3tc_footer_comment'
54
+ ) );
55
+ }
56
+
57
+ public function w3tc_config_default_values( $default_values ) {
58
+ $default_values['newrelic'] = array(
59
+ 'monitoring_type' => 'apm',
60
+ 'accept.logged_roles' => true,
61
+ 'accept.roles' => array( 'contributor' ),
62
+ 'use_php_function' => true,
63
+ 'cache_time' => 5,
64
+ 'include_rum' => true
65
+ );
66
+
67
+ return $default_values;
68
+ }
69
+
70
+ function ob_callback_browser( $buffer ) {
71
+ $core = Dispatcher::component( 'Extension_NewRelic_Core' );
72
+ $app = $core->get_effective_browser_application();
73
+ if ( isset( $app['loader_script'] ) ) {
74
+ $buffer = preg_replace( '~<head(\s+[^>]*)*>~Ui',
75
+ '\\0' . $app['loader_script'], $buffer, 1 );
76
+ }
77
+
78
+ return $buffer;
79
+ }
80
+
81
+ function ob_callback_apm( $buffer ) {
82
+ if ( $this->_config->get_boolean( array( 'newrelic', 'include_rum' ) ) ) {
83
+ if ( ( $this->_config->get_boolean( 'browsercache.html.compression' ) ||
84
+ $this->_config->get_string( 'pgcache.engine' ) == 'file_generic' ) && !$this->_should_disable_auto_rum() ) {
85
+ $buffer = preg_replace( '~<head(\s+[^>]*)*>~Ui', '\\0' . \NewRelicWrapper::get_browser_timing_header(), $buffer, 1 );
86
+ $buffer = preg_replace( '~<\\/body>~', \NewRelicWrapper::get_browser_timing_footer() . '\\0', $buffer, 1 );
87
+ }
88
+ }
89
+ return $buffer;
90
+ }
91
+
92
+ /**
93
+ * Mark current transaction as an background job in New Relic.
94
+ */
95
+ function background_task() {
96
+ \NewRelicWrapper::mark_as_background_job();
97
+ }
98
+
99
+ /**
100
+ * Disable auto rum for current transaction.
101
+ */
102
+ function disable_auto_rum() {
103
+ \NewRelicWrapper::disable_auto_rum();
104
+ }
105
+
106
+ function _should_disable_auto_rum() {
107
+ /**
108
+ * Disable for AJAX so its not messed up
109
+ */
110
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
111
+ $this->newrelic_reject_reason = __( 'DOING_AJAX constant is defined', 'w3-total-cache' );
112
+
113
+ return true;
114
+ }
115
+
116
+
117
+ /**
118
+ * Check for DONOTAUTORUM constant
119
+ */
120
+ if ( defined( 'DONOTAUTORUM' ) && DONOTAUTORUM ) {
121
+ $this->newrelic_reject_reason = __( 'DONOTAUTORUM constant is defined', 'w3-total-cache' );
122
+
123
+ return true;
124
+ }
125
+
126
+ /**
127
+ * Check logged users roles
128
+ */
129
+ if ( $this->_config->get_boolean( array( 'newrelic', 'accept.logged_roles' ) ) &&
130
+ $this->_check_logged_in_role_not_allowed() ) {
131
+ $this->newrelic_reject_reason = __( 'logged in role is rejected',
132
+ 'w3-total-cache' );
133
+
134
+ return true;
135
+ }
136
+
137
+ return false;
138
+ }
139
+
140
+ /**
141
+ * Check if logged in user role is allowed to use New Relic Auto RUM
142
+ *
143
+ * @return boolean
144
+ */
145
+ private function _check_logged_in_role_not_allowed() {
146
+ global $current_user;
147
+
148
+ if ( !is_user_logged_in() )
149
+ return false;
150
+
151
+ $roles = $this->_config->get_array( array( 'newrelic', 'accept.roles' ) );
152
+
153
+ if ( empty( $roles ) || empty( $current_user->roles ) ||
154
+ !is_array( $current_user->roles ) )
155
+ return true;
156
+
157
+ foreach ( $current_user->roles as $role ) {
158
+ if ( in_array( $role, $roles ) )
159
+ return false;
160
+ }
161
+
162
+ return true;
163
+ }
164
+
165
+ public function set_appname() {
166
+ static $appname_set;
167
+ if ( !$appname_set && ( $this->_config->get_boolean( array( 'newrelic', 'use_php_function' ) ) || Util_Environment::is_wpmu() ) ) {
168
+ $appname_set = true;
169
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
170
+ $appname = $service->get_effective_appname();
171
+
172
+ $enable_xmit = $this->_config->get_boolean( array( 'newrelic', 'enable_xmit' ) );
173
+ \NewRelicWrapper::set_appname( $appname, '', $enable_xmit );
174
+ }
175
+ }
176
+
177
+ public function w3tc_footer_comment( $strings ) {
178
+ $append = ( $this->newrelic_reject_reason != '' ) ?
179
+ sprintf( ' (%s)', $this->newrelic_reject_reason ) : '';
180
+ $strings[] = sprintf(
181
+ __( "Application Monitoring using New Relic%s", 'w3-total-cache' ),
182
+ $append );
183
+
184
+ return $strings;
185
+ }
186
+ }
187
+
188
+
189
+
190
+ $p = new Extension_NewRelic_Plugin();
191
+ $p->run();
192
+
193
+ if ( is_admin() ) {
194
+ $p = new Extension_NewRelic_Plugin_Admin();
195
+ $p->run();
196
+ }
Extension_NewRelic_Plugin_Admin.php ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_Plugin_Admin {
7
+ private $_config;
8
+
9
+ /**
10
+ *
11
+ *
12
+ * @param unknown $extensions
13
+ * @param Config $config
14
+ * @return mixed
15
+ */
16
+ static public function w3tc_extensions( $extensions, $config ) {
17
+ $extensions['newrelic'] = array (
18
+ 'name' => 'New Relic',
19
+ 'author' => 'W3 EDGE',
20
+ 'description' => __( 'New Relic is software analytics platform offering app performance management and mobile monitoring solutions.', 'w3-total-cache' ),
21
+ 'author_uri' => 'https://www.w3-edge.com/',
22
+ 'extension_uri' => 'https://www.w3-edge.com/',
23
+ 'extension_id' => 'newrelic',
24
+ 'version' => '1.0',
25
+ 'enabled' => true,
26
+ 'requirements' => '',
27
+ 'active_frontend_own_control' => true,
28
+ 'path' => 'w3-total-cache/Extension_NewRelic_Plugin.php'
29
+ );
30
+
31
+ return $extensions;
32
+ }
33
+
34
+
35
+
36
+ function __construct() {
37
+ $this->_config = Dispatcher::config();
38
+ }
39
+
40
+
41
+
42
+ function run() {
43
+ add_filter( 'w3tc_compatibility_test', array(
44
+ $this, 'verify_compatibility' ) );
45
+ add_action( 'w3tc_config_save', array( $this, 'w3tc_config_save' ), 10, 1 );
46
+
47
+ add_filter( 'w3tc_admin_actions', array( $this, 'w3tc_admin_actions' ) );
48
+ add_filter( 'w3tc_admin_menu', array( $this, 'w3tc_admin_menu' ) );
49
+ add_filter( 'w3tc_extension_plugin_links_newrelic',
50
+ array( $this, 'w3tc_extension_plugin_links' ) );
51
+ add_action( 'w3tc_settings_page-w3tc_monitoring',
52
+ array( $this, 'w3tc_settings_page_w3tc_monitoring' ) );
53
+
54
+ add_action( 'admin_init_w3tc_general', array(
55
+ '\W3TC\Extension_NewRelic_GeneralPage',
56
+ 'admin_init_w3tc_general'
57
+ ) );
58
+ add_action( 'w3tc_ajax', array(
59
+ '\W3TC\Extension_NewRelic_Popup',
60
+ 'w3tc_ajax'
61
+ ) );
62
+
63
+ if ( Util_Admin::is_w3tc_admin_page() ) {
64
+ add_action( 'admin_notices', array(
65
+ $this,
66
+ 'admin_notices'
67
+ ) );
68
+ add_action( 'network_admin_notices', array(
69
+ $this,
70
+ 'admin_notices'
71
+ ) );
72
+ }
73
+
74
+ add_action( 'admin_init_w3tc_dashboard', array(
75
+ '\W3TC\Extension_NewRelic_Widget',
76
+ 'admin_init_w3tc_dashboard' ) );
77
+ add_action( 'w3tc_ajax', array(
78
+ '\W3TC\Extension_NewRelic_Widget',
79
+ 'w3tc_ajax' ) );
80
+
81
+ add_filter( 'w3tc_notes', array( $this, 'w3tc_notes' ) );
82
+ }
83
+
84
+
85
+
86
+ public function w3tc_admin_menu( $menu ) {
87
+ $c = Dispatcher::config();
88
+ $monitoring_type = $c->get_string( array( 'newrelic', 'monitoring_type' ) );
89
+ if ( $monitoring_type == 'apm' ) {
90
+ $menu = array_merge( $menu, array(
91
+ 'w3tc_monitoring' => array(
92
+ 'page_title' => __( 'Monitoring', 'w3-total-cache' ),
93
+ 'menu_text' => __( 'Monitoring', 'w3-total-cache' ),
94
+ 'visible_always' => false
95
+ )
96
+ ) );
97
+ }
98
+
99
+ return $menu;
100
+ }
101
+
102
+
103
+
104
+ public function w3tc_admin_actions( $handlers ) {
105
+ $handlers['new_relic'] = 'Extension_NewRelic_AdminActions';
106
+ return $handlers;
107
+ }
108
+
109
+
110
+
111
+ public function w3tc_extension_plugin_links( $links ) {
112
+ $links = array();
113
+ $links[] = '<a class="edit" href="' .
114
+ esc_attr( Util_Ui::admin_url( 'admin.php?page=w3tc_general#monitoring' ) ) .
115
+ '">'. __( 'Settings' ).'</a>';
116
+
117
+ return $links;
118
+ }
119
+
120
+
121
+
122
+ public function w3tc_settings_page_w3tc_monitoring() {
123
+ $v = new Extension_NewRelic_Page();
124
+ $v->render_content();
125
+ }
126
+
127
+
128
+
129
+ function admin_notices() {
130
+ $api_key = $this->_config->get_string( array( 'newrelic', 'api_key' ) );
131
+ if ( empty( $api_key ) )
132
+ return;
133
+
134
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
135
+
136
+ $verify_running_result = $nerser->verify_running();
137
+ $not_running = is_array( $verify_running_result );
138
+
139
+ if ( $not_running ) {
140
+ $message = '<p>' .
141
+ __( 'New Relic is not running correctly. ', 'w3-total-cache' ) .
142
+ '<a href="#" class="w3tc_link_more {for_class: \'w3tc_nr_admin_notice\'}">' .
143
+ 'more</a> ' .
144
+ '<div class="w3tc_none w3tc_nr_admin_notice">' .
145
+ __( 'The plugin has detected the following issues:. ', 'w3-total-cache' );
146
+ $message .= "<ul class=\"w3-bullet-list\">\n";
147
+ foreach ( $verify_running_result as $cause ) {
148
+ $message .= "<li>$cause</li>";
149
+ }
150
+ $message .= "</ul>\n";
151
+
152
+ $message .= '<p>' . sprintf(
153
+ __( 'Please review the <a href="%s">settings</a>.', 'w3-total-cache' ),
154
+ network_admin_url( 'admin.php?page=w3tc_general#monitoring' ) ) . "</p>";
155
+ $message .= "</div></p>\n";
156
+
157
+ Util_Ui::error_box( $message );
158
+ }
159
+ }
160
+
161
+
162
+
163
+ function w3tc_notes( $notes ) {
164
+ $newrelic_notes = Dispatcher::component( 'Extension_NewRelic_AdminNotes' );
165
+ $notes = array_merge( $notes,
166
+ $newrelic_notes->notifications( $this->_config ) );
167
+
168
+ return $notes;
169
+ }
170
+
171
+
172
+
173
+ /**
174
+ * Returns a list of the verification status of the the new relic
175
+ * requirements. To be used on the compatibility page
176
+ *
177
+ * @param unknown $verified_list
178
+ * @return array
179
+ */
180
+ function verify_compatibility( $verified_list ) {
181
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
182
+ $nr_verified = $nerser->verify_compatibility();
183
+ $verified_list[] = '<strong>New Relic</strong>';
184
+ foreach ( $nr_verified as $criteria => $result )
185
+ $verified_list[] = sprintf( "$criteria: %s", $result );
186
+ return $verified_list;
187
+ }
188
+
189
+
190
+
191
+ public function w3tc_config_save( $config ) {
192
+ // frontend activity
193
+ $api_key = $config->get_string( array( 'newrelic', 'api_key' ) );
194
+ $is_filled = !empty( $api_key );
195
+
196
+ if ( $is_filled ) {
197
+ $monitoring_type = $config->get_string( array(
198
+ 'newrelic', 'monitoring_type' ) );
199
+
200
+ if ( $monitoring_type == 'browser' ) {
201
+ $v = $config->get_string( array( 'newrelic',
202
+ 'browser.application_id' ) );
203
+ $is_filled = !empty( $v );
204
+ } else {
205
+ $v = $config->get_string( array( 'newrelic',
206
+ 'apm.application_name' ) );
207
+ $is_filled = !empty( $v );
208
+ }
209
+ }
210
+
211
+ $config->set_extension_active_frontend( 'newrelic', $is_filled );
212
+ }
213
+ }
Extension_NewRelic_Popup.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_Popup {
7
+ static public function w3tc_ajax() {
8
+ $o = new Extension_NewRelic_Popup();
9
+
10
+ add_action( 'w3tc_ajax_newrelic_popup',
11
+ array( $o, 'w3tc_ajax_newrelic_popup' ) );
12
+ add_action( 'w3tc_ajax_newrelic_list_applications',
13
+ array( $o, 'w3tc_ajax_newrelic_list_applications' ) );
14
+ add_action( 'w3tc_ajax_newrelic_apply_configuration',
15
+ array( $o, 'w3tc_ajax_newrelic_apply_configuration' ) );
16
+ }
17
+
18
+
19
+
20
+ public function w3tc_ajax_newrelic_popup() {
21
+ $c = Dispatcher::config();
22
+
23
+ $this->render_intro( array(
24
+ 'api_key' => $c->get_string( array( 'newrelic', 'api_key' ) )
25
+ ) );
26
+ }
27
+
28
+
29
+
30
+ private function render_intro( $details ) {
31
+ include W3TC_DIR . '/Extension_NewRelic_Popup_View_Intro.php';
32
+ }
33
+
34
+
35
+
36
+ public function w3tc_ajax_newrelic_list_applications() {
37
+ $api_key = $_REQUEST['api_key'];
38
+
39
+ $c = Dispatcher::config();
40
+ $details = array(
41
+ 'api_key' => $api_key,
42
+ 'monitoring_type' => $c->get_string( array(
43
+ 'newrelic', 'monitoring_type' ) ),
44
+ 'apm.application_name' => $c->get_string( array(
45
+ 'newrelic', 'apm.application_name' ) ),
46
+ 'browser.application_id' => $c->get_string( array(
47
+ 'newrelic', 'browser.application_id' ) )
48
+ );
49
+ if ( $details['monitoring_type'] != 'browser' )
50
+ $details['monitoring_type'] = 'apm';
51
+
52
+ $service = new Extension_NewRelic_Service( $api_key );
53
+
54
+ try {
55
+ $details['apm_applications'] = $service->get_applications();
56
+
57
+ $api = new Extension_NewRelic_Api( $api_key );
58
+ $details['browser_applications'] = $api->get_browser_applications();
59
+ } catch ( \Exception $ex ) {
60
+ $details = array(
61
+ 'api_key' => $api_key,
62
+ 'error_message' => 'API key verification failed: ' .
63
+ $ex->getMessage()
64
+ );
65
+ $this->render_intro( $details );
66
+ return;
67
+ }
68
+
69
+ $details['browser_disabled'] = !Util_Environment::is_w3tc_pro( $c );
70
+
71
+ include W3TC_DIR . '/Extension_NewRelic_Popup_View_ListApplications.php';
72
+ }
73
+
74
+
75
+
76
+ public function w3tc_ajax_newrelic_apply_configuration() {
77
+ $api_key = $_REQUEST['api_key'];
78
+ $monitoring_type = Util_Request::get( 'monitoring_type', 'apm' );
79
+ $apm_application_name = Util_Request::get( 'apm_application_name' );
80
+ $browser_application_id = Util_Request::get( 'browser_application_id' );
81
+
82
+ $c = Dispatcher::config();
83
+ $c->set( array( 'newrelic', 'api_key' ), $api_key );
84
+
85
+ if ( $monitoring_type == 'apm' ) {
86
+ $c->set( array( 'newrelic', 'monitoring_type' ), 'apm' );
87
+ $c->set( array( 'newrelic', 'apm.application_name' ), $apm_application_name );
88
+ } else {
89
+ $c->set( array( 'newrelic', 'monitoring_type' ), 'browser' );
90
+ $c->set( array( 'newrelic', 'browser.application_id' ),
91
+ $browser_application_id );
92
+ }
93
+
94
+ $c->save();
95
+
96
+ // flush cached values on api key change to allow to reset it from ui
97
+ // if something goes wrong
98
+ update_option( 'w3tc_nr_account_id', '' );
99
+ update_option( 'w3tc_nr_application_id', '' );
100
+
101
+ $postfix = Util_Admin::custom_message_id( array(),
102
+ array( 'newrelic_configuration_saved' =>
103
+ 'NewRelic configuration is saved successfully' ) );
104
+ echo 'Location admin.php?page=w3tc_general&' . $postfix;
105
+ }
106
+ }
Extension_NewRelic_Popup_View.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ $('body')
3
+ .on('click', '.w3tcnr_configure', function() {
4
+ W3tc_Lightbox.open({
5
+ id:'w3tc-overlay',
6
+ close: '',
7
+ width: 800,
8
+ height: 400,
9
+ url: ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
10
+ '&w3tc_action=newrelic_popup',
11
+ });
12
+ })
13
+
14
+
15
+
16
+ .on('click', '.w3tcnr_list_applications', function() {
17
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
18
+ '&w3tc_action=newrelic_list_applications&api_key=' +
19
+ encodeURIComponent($('.w3tcnr_api_key').val());
20
+ W3tc_Lightbox.load(url);
21
+ })
22
+
23
+
24
+
25
+ .on('click', '.w3tcnr_apply_configuration', function() {
26
+ var url = ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
27
+ '&w3tc_action=newrelic_apply_configuration';
28
+ $('.w3tcnr_form').find('input').each(function(i) {
29
+ var name = $(this).attr('name');
30
+ var type = $(this).attr('type');
31
+ if (type == 'radio') {
32
+ if (!$(this).attr('checked'))
33
+ return;
34
+ }
35
+
36
+ if (name)
37
+ url += '&' + encodeURIComponent(name) + '=' +
38
+ encodeURIComponent($(this).val());
39
+ });
40
+ $('.w3tcnr_form').find('select').each(function(i) {
41
+ var name = $(this).attr('name');
42
+ url += '&' + encodeURIComponent(name) + '=' +
43
+ encodeURIComponent($(this).val());
44
+ });
45
+
46
+ W3tc_Lightbox.load(url);
47
+ });
48
+ });
Extension_NewRelic_Popup_View_Intro.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form style="padding: 20px">
8
+ <?php
9
+ if ( isset( $details['error_message'] ) )
10
+ echo '<div class="error">' . $details['error_message'] . '</div>';
11
+ ?>
12
+
13
+ <div class="metabox-holder">
14
+ <?php Util_Ui::postbox_header(
15
+ __( 'Specify API Key', 'w3-total-cache' ) ); ?>
16
+ <table class="form-table">
17
+ <tr>
18
+ <th>
19
+ <label for="newrelic_api_key"><?php
20
+ _e( '<acronym title="Application Programming Interface">API</acronym> key:', 'w3-total-cache' )
21
+ ?></label>
22
+ </th>
23
+ <td>
24
+ <input name="api_key" class="w3tcnr_api_key" type="text"
25
+ value="<?php echo esc_attr( $details['api_key'] ) ?>" size="45"/>
26
+ </td>
27
+ </tr>
28
+ </table>
29
+
30
+ <p class="submit">
31
+ <input type="button"
32
+ class="w3tcnr_list_applications w3tc-button-save button-primary"
33
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
34
+ </p>
35
+ <?php Util_Ui::postbox_footer(); ?>
36
+ </div>
37
+ </form>
Extension_NewRelic_Popup_View_ListApplications.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+ ?>
7
+ <form style="padding: 20px" class="w3tcnr_form">
8
+ <?php
9
+ Util_Ui::hidden( '', 'api_key', $details['api_key'] );
10
+ ?>
11
+
12
+ <div class="metabox-holder">
13
+ <?php Util_Ui::postbox_header(
14
+ __( 'Select Application', 'w3-total-cache' ) ); ?>
15
+ <table class="form-table">
16
+ <tr><td>
17
+ <label>
18
+ <input name="monitoring_type" type="radio" value="apm"
19
+ <?php checked( $details['monitoring_type'], 'apm' ) ?> />
20
+ APM application (uses NewRelic PHP module)
21
+ </label><br />
22
+ <select name="apm_application_name" class="w3tcnr_apm">
23
+ <?php
24
+ foreach ( $details['apm_applications'] as $a ) {
25
+ echo '<option ';
26
+ selected( $a, $details['apm.application_name'] );
27
+ echo '>' . htmlspecialchars( $a ) . '</option>';
28
+ }
29
+ ?>
30
+ </select>
31
+ </td></tr>
32
+ <tr><td>
33
+ <label>
34
+ <input name="monitoring_type" type="radio" value="browser"
35
+ <?php checked( $details['monitoring_type'], 'browser' ) ?>
36
+ <?php disabled( $details['browser_disabled'] ) ?> />
37
+ Standalone Browser
38
+ <?php
39
+ if ( $details['browser_disabled'] )
40
+ echo ' (W3TC Pro Only)';
41
+ ?>
42
+ </label><br />
43
+ <select name="browser_application_id" class="w3tcnr_browser">
44
+ <?php
45
+ foreach ( $details['browser_applications'] as $a ) {
46
+ echo '<option value="' . esc_attr( $a['id'] ) . '" ';
47
+ selected( $a['id'], $details['browser.application_id'] );
48
+ echo '>' . htmlspecialchars( $a['name'] ) . '</option>';
49
+ }
50
+ ?>
51
+ </select>
52
+ </td></tr>
53
+ </table>
54
+
55
+ <p class="submit">
56
+ <input type="button"
57
+ class="w3tcnr_apply_configuration w3tc-button-save button-primary"
58
+ value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
59
+ </p>
60
+ <?php Util_Ui::postbox_footer(); ?>
61
+ </div>
62
+ </form>
Extension_NewRelic_Service.php ADDED
@@ -0,0 +1,632 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * Wrapper for NewRelicAPI.
6
+ * deprecated
7
+ *
8
+ * @see NewRelicAPI
9
+ */
10
+ class Extension_NewRelic_Service {
11
+ private $_api_key;
12
+ private $_cache_time;
13
+
14
+ /**
15
+ * Checks Config for the params if they are not provided in the constructor.
16
+ *
17
+ * @param string $api_key
18
+ * @param string $account_id
19
+ * @param string $application_id
20
+ */
21
+ function __construct( $api_key = '' ) {
22
+ $config = Dispatcher::config();
23
+ if ( $api_key )
24
+ $this->_api_key = $api_key;
25
+ else
26
+ $this->_api_key = $config->get_string( array( 'newrelic', 'api_key' ) );
27
+
28
+ $this->_cache_time = $config->get_integer( array( 'newrelic', 'cache_time' ), 5 );
29
+ if ( $this->_cache_time < 1 )
30
+ $this->_cache_time = 5;
31
+ }
32
+
33
+ /**
34
+ * Checks if the platform running WP is supported by New Relic.
35
+ * The verifications is based on https://newrelic.com/docs/php/new-relic-for-php
36
+ *
37
+ * @return array
38
+ */
39
+ function verify_compatibility() {
40
+ $php_versions = array( '5.2.x', '5.3.x', '5.4.x' );
41
+ $verified = array();
42
+ $version = explode( '.', PHP_VERSION );
43
+ $php_version = sprintf( '%s.%s.%s', $version[0], $version[1], $version[2] );
44
+ $php_version_ok = ( version_compare( $php_version, '5.2', '>' ) &&
45
+ version_compare( $php_version, '5.5', '<' ) );
46
+
47
+ $supported_string = __( 'Supported', 'w3-total-cache' );
48
+
49
+ $verified[__( 'PHP version', 'w3-total-cache' )] =
50
+ ( $php_version_ok ? $supported_string :
51
+ sprintf( __( 'Not supported: %s. Supported versions are %s.', 'w3-total-cache' ),
52
+ $php_version, implode( ', ', $php_versions ) ) );
53
+
54
+ $os_name = php_uname( 's' );
55
+ switch ( $os_name ) {
56
+ case 'Linux':
57
+ /**
58
+ * Any other version of Linux with kernel 2.6.13 or later
59
+ * (2.6.26 and later highly recommended) and glibc 2.5 or later
60
+ */
61
+ $version = explode( '.', php_uname( 'r' ) );
62
+ $os_version = sprintf( '%d.%d.%d', $version[0], $version[1], $version[2] );
63
+ $os_check = version_compare( $os_version, '2.6.13', '>=' );
64
+ break;
65
+ case 'FreeBSD':
66
+ /**
67
+ * You must enable the linkthr build option so that the New Relic agent will not cause your PHP to hang.
68
+ */
69
+ $version = explode( '.', php_uname( 'r' ) );
70
+ $os_version = sprintf( '%d.%d', $version[0], $version[1] );
71
+ $os_check = version_compare( $os_version, '7.3', '>=' );
72
+ break;
73
+ case 'MacOS/X':
74
+ /**
75
+ * MacOS/X configurations do not use the standard /etc/init.d/newrelic-daemon script.
76
+ * Instead, they use /usr/bin/newrelic-daemon-service in the same way; for example:
77
+ * /usr/bin/newrelic-daemon-service restart.
78
+ */
79
+ $version = explode( '.', php_uname( 'r' ) );
80
+ $os_version = sprintf( '%d.%d', $version[0], $version[1] );
81
+ $os_check = version_compare( $os_version, '10.5', '>=' );
82
+ break;
83
+ case 'Open Solaris':
84
+ /**
85
+ * snv_134b or later
86
+ */
87
+ $version = explode( '.', php_uname( 'r' ) );
88
+ $os_version = sprintf( '%d', $version[0] );
89
+ $os_check = version_compare( $os_version, '10', '==' );
90
+ break;
91
+ default:
92
+ $os_check = false;
93
+ $os_name = php_uname();
94
+ $os_version = '';
95
+ }
96
+
97
+ $verified[__( 'Operating System', 'w3-total-cache' )] = ( $os_check ) ? $supported_string :
98
+ sprintf( __( 'Not Supported. (%s %s See %s page.)', 'w3-total-cache' ),
99
+ $os_name, $os_version,
100
+ '<a href="https://newrelic.com/docs/php/new-relic-for-php#requirements" target="_blank">
101
+ NewRelic Requirements</a>'
102
+ );
103
+
104
+ /**
105
+ * Apache 2.2 or 2.4 via mod_php
106
+ * Or any web server that supports FastCGI using php-fpm
107
+ */
108
+ $server = explode( '/', $_SERVER['SERVER_SOFTWARE'] );
109
+ $ws_check = false;
110
+ $ws_name = $_SERVER['SERVER_SOFTWARE'];
111
+ $ws_version = '';
112
+
113
+ if ( sizeof( $server ) > 1 ) {
114
+ $ws_name = $server[0];
115
+ $ws_version = $server[1];
116
+ if ( sizeof( $version = explode( '.', $ws_version ) )>1 )
117
+ $ws_version = sprintf( '%d.%d', $version[0], $version[1] );
118
+ }
119
+ switch ( true ) {
120
+ case Util_Environment::is_apache():
121
+ if ( $ws_version )
122
+ $ws_check = version_compare( $ws_version, '2.2', '>=' ) || version_compare( $ws_version, '2.4', '>=' );
123
+ break;
124
+ case Util_Environment::is_nginx():
125
+ $ws_check = php_sapi_name() == 'fpm-fcgi';
126
+ $ws_name .= php_sapi_name();
127
+ break;
128
+ default:
129
+ $ws_check = php_sapi_name() == 'fpm-fcgi';
130
+ $ws_name = $_SERVER['SERVER_SOFTWARE'];
131
+ $ws_version = '';
132
+ }
133
+ $verified[__( 'Web Server', 'w3-total-cache' )] = $ws_check ? $supported_string :
134
+ sprintf( __( 'Not Supported. (%s %s See %s page.)', 'w3-total-cache' ),
135
+ $ws_name, $ws_version,
136
+ '<a href="https://newrelic.com/docs/php/new-relic-for-php#requirements" target="_blank">
137
+ NewRelic Requirements</a>'
138
+ );
139
+ return $verified;
140
+ }
141
+
142
+ /**
143
+ * Verifies that detectable New Relic functionality is running and configured properly.
144
+ * Returns array with what is wrong if verification fails.
145
+ *
146
+ * @return array|bool
147
+ */
148
+ public function verify_running() {
149
+ $config = Dispatcher::config();
150
+
151
+ $error = array();
152
+ if ( !$this->get_api_key() )
153
+ $error['api_key'] = __( 'API Key is not configured.', 'w3-total-cache' );
154
+
155
+
156
+ if ( $config->get( array( 'newrelic', 'monitoring_type' ) ) == 'browser' ) {
157
+ $name = $this->get_effective_appname();
158
+ if ( empty( $name ) )
159
+ $error['application_id'] = __( 'Application ID is not configured. Enter/Select application name.', 'w3-total-cache' );
160
+ } else {
161
+ if ( !$this->module_is_enabled() )
162
+ $error['module_enabled'] = __( 'PHP module is not enabled.', 'w3-total-cache' );
163
+ if ( !$this->agent_enabled() )
164
+ $error['agent_enabled'] = __( 'PHP agent is not enabled.', 'w3-total-cache' );
165
+
166
+ if ( !$this->get_account_id() )
167
+ $error['account_id'] = __( 'Account ID is not configured.', 'w3-total-cache' );
168
+ if ( $this->get_effective_application_id() == 0 )
169
+ $error['application_id'] = __( 'Application ID is not configured. Enter/Select application name.', 'w3-total-cache' );
170
+ try {
171
+ if ( !$this->get_license_key_from_ini() )
172
+ $error['license'] = __( 'License key could not be detected in ini file.', 'w3-total-cache' );
173
+ $licences = explode( ' ', trim( $this->get_license_key_from_ini() ) );
174
+ $licences = array_map( 'trim', $licences );
175
+ if ( $this->get_license_key_from_ini() && $this->get_license_key_from_account()
176
+ && !in_array( trim( $this->get_license_key_from_account() ), $licences ) )
177
+ $error['license'] = sprintf( __( 'Configured license key does not match license key(s) in account: <br />%s <br />%s', 'w3-total-cache' )
178
+ , $this->get_license_key_from_ini()
179
+ , implode( '<br />', $licences ) );
180
+ $this->get_account_id();
181
+ } catch ( \Exception $ex ) {
182
+ $error['api_key'] = __( 'API Key is invalid.', 'w3-total-cache' );
183
+ }
184
+ }
185
+
186
+ return $error ? $error : true;
187
+ }
188
+
189
+ /**
190
+ * Checks the ini or conf file to see if newrelic is enabled.
191
+ *
192
+ * @return string
193
+ */
194
+ public function agent_enabled() {
195
+ return ini_get( 'newrelic.enabled' );
196
+ }
197
+
198
+ /**
199
+ * Checks if the New Relic PHP module is enabled
200
+ *
201
+ * @return bool
202
+ */
203
+ public function module_is_enabled() {
204
+ return function_exists( 'newrelic_set_appname' );
205
+ }
206
+
207
+ /**
208
+ * Retrieves the configured license key in ini/conf files.
209
+ *
210
+ * @return string
211
+ */
212
+ public function get_license_key_from_ini() {
213
+ return ini_get( 'newrelic.license' );
214
+ }
215
+
216
+ /**
217
+ * Returns the API key
218
+ *
219
+ * @return string
220
+ */
221
+ public function get_api_key() {
222
+ return $this->_api_key;
223
+ }
224
+
225
+ /**
226
+ * Returns a NewRelicAPI instance depending on configured params.
227
+ *
228
+ * @param int $api_key
229
+ * @return NewRelicAPI
230
+ */
231
+ private function getAPI() {
232
+ static $api = null;
233
+ if ( !$api ) {
234
+ require_once W3TC_LIB_NEWRELIC_DIR . '/NewRelicAPI.php';
235
+ $api = new \NewRelicAPI( $this->_api_key );
236
+ }
237
+
238
+ return $api;
239
+ }
240
+
241
+ /**
242
+ * Retrieves an array with all applications.
243
+ *
244
+ * @param unknown $account_id
245
+ * @return array
246
+ */
247
+ public function get_applications() {
248
+ if ( empty( $this->_api_key ) )
249
+ return array();
250
+
251
+ return $this->getAPI()->get_applications( $this->get_account_id() );
252
+ }
253
+
254
+ public function get_browser_applications() {
255
+ return $this->getAPI()->get_browser_applications();
256
+ }
257
+
258
+ /**
259
+ * Retrieves a specific application
260
+ *
261
+ * @param string $application_id
262
+ * @return mixed
263
+ */
264
+ public function get_application( $application_id ) {
265
+ $applications = $this->get_applications();
266
+ return $applications[$application_id];
267
+ }
268
+
269
+ /**
270
+ * Retrieves the application summary
271
+ *
272
+ * @param string $application_id
273
+ * @return array
274
+ */
275
+ public function get_application_summary() {
276
+ return $this->getAPI()->get_application_summary(
277
+ $this->get_account_id(), $this->get_effective_application_id() );
278
+ }
279
+
280
+ /**
281
+ * Retrievs the account info connected with the API key
282
+ *
283
+ * @return array|mixed|null
284
+ */
285
+ public function get_account() {
286
+ static $account = null;
287
+ if ( !$account )
288
+ $account = $this->getAPI()->get_account();
289
+ return $account;
290
+ }
291
+
292
+ /**
293
+ * Returns the subscription for the account
294
+ *
295
+ * @return string|null
296
+ */
297
+ public function get_subscription() {
298
+ $account = $this->get_account();
299
+ if ( $account )
300
+ return $account['subscription'];
301
+ return null;
302
+ }
303
+
304
+ /**
305
+ * Checks if account supports retrieval of metrics (names/data)
306
+ *
307
+ * @return bool
308
+ */
309
+ public function can_get_metrics() {
310
+ $subscription = $this->get_subscription();
311
+ return $subscription['product-name'] != 'Lite';
312
+ }
313
+
314
+ /**
315
+ * Retrieves the license key from the account
316
+ *
317
+ * @return null|string
318
+ */
319
+ public function get_license_key_from_account() {
320
+ $account = $this->get_account();
321
+ if ( $account )
322
+ return $account['license-key'];
323
+ return null;
324
+ }
325
+
326
+ /**
327
+ * Retrieves the application setting. Cached for 5 minutes.
328
+ *
329
+ * @return array|mixed
330
+ */
331
+ public function get_application_settings() {
332
+ $settings = $this->getAPI()->get_application_settings(
333
+ $this->get_account_id(), $this->get_effective_application_id() );
334
+
335
+ return $settings;
336
+ }
337
+
338
+ /**
339
+ * Update applications settings
340
+ *
341
+ * @param array $application
342
+ * @return bool
343
+ */
344
+ public function update_application_settings( $application ) {
345
+ $result = $this->getAPI()->update_application_settings(
346
+ $this->get_account_id(), $this->get_effective_application_id(),
347
+ $application );
348
+ return $result;
349
+ }
350
+
351
+ /**
352
+ * Retrieves metric names all or those matching regex with limit. Result is cached.
353
+ *
354
+ * @param string $regex
355
+ * @param string $limit
356
+ * @return array|mixed
357
+ */
358
+ public function get_metric_names( $regex = '', $limit = '' ) {
359
+ $metric_names_object = $this->getAPI()->get_metric_names( $this->get_effective_application_id(), $regex, $limit );
360
+ if ( !$metric_names_object )
361
+ return array();
362
+
363
+ $metric_names = array();
364
+ foreach ( $metric_names_object as $metric ) {
365
+ $metric_names[$metric->name] = $metric;
366
+ }
367
+
368
+ return $metric_names;
369
+ }
370
+
371
+ /**
372
+ * Retrieves metric data for the provided metrics
373
+ *
374
+ * @param array $metrics
375
+ * @param string $field metric value field. If a metric name does not have this field the metric name is excluded
376
+ * @param int $days
377
+ * @param bool $summary
378
+ * @param bool $use_subgroup
379
+ * @return array|mixed
380
+ */
381
+ public function get_metric_data( $metrics, $field, $days=7, $summary = true,
382
+ $use_subgroup = true ) {
383
+ if ( !is_array( $metrics ) )
384
+ $metrics = array( $metrics );
385
+
386
+ $begin = new \DateTime( gmdate( "Y-m-d G:i:s",
387
+ strtotime( ( $days>1 ? "-$days days" : "-$days day" ) ) ) );
388
+ $beginStr = $begin->format( 'Y-m-d' ) . 'T' . $begin->format( 'H:i:s' ) . 'Z';
389
+ $to = new \DateTime( gmdate( "Y-m-d G:i:s" ) );
390
+ $toStr = $to->format( 'Y-m-d' ) . 'T' . $to->format( 'H:i:s' ) . 'Z';
391
+ $cache_key = md5( implode( ',', array( $this->get_account_id(),
392
+ $this->get_effective_application_id(), $beginStr, $toStr, implode( ',', $metrics ),
393
+ $field, $summary ) ) );
394
+
395
+ $metric_data = $this->getAPI()->get_metric_data( $this->get_account_id(),
396
+ $this->get_effective_application_id(), $beginStr, $toStr, $metrics, $field,
397
+ $summary );
398
+ $formatted_data = array();
399
+
400
+ if ( $metric_data ) {
401
+ foreach ( $metric_data as $metric ) {
402
+ $path = explode( '/', $metric->name );
403
+ $group = $path[0];
404
+ if ( $use_subgroup ) {
405
+ $subgroup = isset( $path[1] ) ? ( $path[1] == 'all' ? 0 : $path[1] ): 0;
406
+ $formatted_data[$group][$subgroup][] = $metric;
407
+ } else {
408
+ $formatted_data[$group][] = $metric;
409
+ }
410
+ }
411
+ }
412
+
413
+ return $formatted_data;
414
+ }
415
+
416
+ /**
417
+ * Retrieves the metrics used for the New Relic Dashboard widget
418
+ *
419
+ * @return array|mixed
420
+ */
421
+ public function get_dashboard_metrics() {
422
+ $metrics = array( 'Database/all', 'WebTransaction', 'EndUser' );
423
+ $field = 'average_response_time';
424
+ return $this->get_metric_data( $metrics, $field, 1, true );
425
+ }
426
+
427
+ /**
428
+ * Retrieves the top 5 pages with slowest page load
429
+ *
430
+ * @return array
431
+ */
432
+ public function get_slowest_page_load() {
433
+ $metric_names = $this->get_metric_names( 'EndUser/WebTransaction/WebTransaction/' );
434
+
435
+ $metric_names_keys = array_keys( $metric_names );
436
+ $metric_data = $this->get_metric_data( $metric_names_keys,
437
+ 'average_response_time', 1 );
438
+ $slowest = array();
439
+
440
+ if ( $metric_data ) {
441
+ $transactions = $metric_data['EndUser']['WebTransaction'];
442
+ foreach ( $transactions as $transaction ) {
443
+ $key = str_replace( 'EndUser/WebTransaction/WebTransaction', '',
444
+ $transaction->name );
445
+ $slowest[$key] = $transaction->average_response_time;
446
+ }
447
+ $slowest = $this->_sort_and_slice( $slowest, 5 );
448
+ }
449
+ return $slowest;
450
+ }
451
+
452
+ /**
453
+ * Retrieves the top 5slowest webtransactions
454
+ *
455
+ * @return array
456
+ */
457
+ public function get_slowest_webtransactions() {
458
+ $metric_names = $this->get_metric_names( '^WebTransaction/' );
459
+ $metric_names_keys = array_keys( $metric_names );
460
+ $metric_data = $this->get_metric_data( $metric_names_keys, 'average_response_time', 1 );
461
+ $slowest = array();
462
+ if ( $metric_data ) {
463
+ $transactions = $metric_data['WebTransaction'];
464
+ foreach ( $transactions as $transaction ) {
465
+ foreach ( $transaction as $tr_sub ) {
466
+ $key = str_replace( 'WebTransaction', '', $tr_sub->name );
467
+ $slowest[$key] = $tr_sub->average_response_time;
468
+ }
469
+ }
470
+ $slowest = $this->_sort_and_slice( $slowest, 5 );
471
+ }
472
+ return $slowest;
473
+ }
474
+
475
+ /**
476
+ * Retrieves the top 5 slowest database queries
477
+ *
478
+ * @return array
479
+ */
480
+ public function get_slowest_database() {
481
+ $metric_names = $this->get_metric_names( '^Database/' );
482
+ $metric_names_keys = array_keys( $metric_names );
483
+ $metric_names_keys = array_slice( $metric_names_keys, 7 );
484
+ $metric_data = $this->get_metric_data( $metric_names_keys, 'average_response_time', 1, true, false );
485
+ $slowest = array();
486
+ if ( $metric_data ) {
487
+ $transactions = $metric_data['Database'];
488
+ foreach ( $transactions as $transaction ) {
489
+ $key = str_replace( 'Database', '', $transaction->name );
490
+ $slowest[$key] = $transaction->average_response_time;
491
+ }
492
+ $slowest = $this->_sort_and_slice( $slowest, 5 );
493
+ }
494
+ return $slowest;
495
+ }
496
+
497
+ /**
498
+ * Retrieves the front end response time
499
+ *
500
+ * @return int
501
+ */
502
+ public function get_frontend_response_time() {
503
+ $metric_data = $this->get_metric_data( 'EndUser',
504
+ 'average_fe_response_time', 1, true, false );
505
+ return isset( $metric_data['EndUser'] ) ?
506
+ $metric_data['EndUser'][0]->average_fe_response_time :
507
+ 0;
508
+ }
509
+
510
+ /**
511
+ * Sorts an array highest to lowest and returns the top $size entries in an array.
512
+ *
513
+ * @param unknown $slowest
514
+ * @param unknown $size
515
+ * @return array
516
+ */
517
+ private function _sort_and_slice( $slowest, $size ) {
518
+ arsort( $slowest, SORT_NUMERIC );
519
+ if ( sizeof( $slowest ) > $size )
520
+ $slowest = array_slice( $slowest, 0, $size );
521
+ return $slowest;
522
+ }
523
+
524
+ /**
525
+ * Retrieves the application name thats used on New Relic
526
+ *
527
+ * @param unknown $application_id
528
+ * @return string
529
+ */
530
+ public function get_application_name( $application_id ) {
531
+ $apps = $this->get_applications( $this->get_account_id() );
532
+ return isset( $apps[$application_id] ) ? $apps[$application_id] : '';
533
+ }
534
+
535
+ /**
536
+ * Retrieves the account id connected with the provided API key
537
+ *
538
+ * @param int $api_key
539
+ * @return int|null
540
+ */
541
+ public function get_account_id() {
542
+ if ( empty( $this->_api_key ) )
543
+ return 0;
544
+
545
+ $ids_string = get_option( 'w3tc_nr_account_id' );
546
+ $ids = @json_decode( $ids_string, true );
547
+ if ( !is_array( $ids ) )
548
+ $ids = array();
549
+
550
+ if ( isset( $ids[$this->_api_key] ) )
551
+ return $ids[$this->_api_key];
552
+
553
+ $ids[$this->_api_key] = 0;
554
+
555
+ try {
556
+ $account = $this->getAPI()->get_account();
557
+
558
+ if ( $account )
559
+ $ids[$this->_api_key] = (int)$account['id'];
560
+ } catch ( \Exception $ex ) {
561
+ return 0;
562
+ }
563
+
564
+ update_option( 'w3tc_nr_account_id', json_encode( $ids ) );
565
+
566
+ return $ids[$this->_api_key];
567
+ }
568
+
569
+ /**
570
+ * Retrieves the application id from New Relic
571
+ *
572
+ * @param unknown $appname
573
+ * @return int|string
574
+ */
575
+ public function get_application_id( $appname ) {
576
+ if ( empty( $appname ) )
577
+ return 0;
578
+
579
+ $apps = $this->get_applications();
580
+ foreach ( $apps as $id => $name ) {
581
+ if ( $name == $appname )
582
+ return $id;
583
+ }
584
+ return 0;
585
+ }
586
+
587
+ public function get_effective_application_id() {
588
+ $config = Dispatcher::config();
589
+
590
+ $monitoring_type = $config->get_string( array( 'newrelic', 'monitoring_type' ) );
591
+ if ( $monitoring_type == 'browser' ) {
592
+ return $config->get_string( array(
593
+ 'newrelic', 'browser.application_id' ) );
594
+ }
595
+
596
+ $appname = $this->get_effective_appname();
597
+ $ids_string = get_option( 'w3tc_nr_application_id' );
598
+ $ids = @json_decode( $ids_string, true );
599
+ if ( !is_array( $ids ) )
600
+ $ids = array();
601
+
602
+ $key = md5( $this->_api_key . $appname );
603
+ if ( isset( $ids[$key] ) )
604
+ return $ids[$key];
605
+
606
+ try {
607
+ $ids[$key] = $this->get_application_id( $appname );
608
+ } catch ( \Exception $ex ) {
609
+ return 0;
610
+ }
611
+
612
+ update_option( 'w3tc_nr_application_id', json_encode( $ids ) );
613
+
614
+ return $ids[$key];
615
+ }
616
+
617
+ public function get_effective_appname() {
618
+ $config = Dispatcher::config();
619
+
620
+ $monitoring_type = $config->get_string( array( 'newrelic', 'monitoring_type' ) );
621
+ if ( $monitoring_type == 'browser' ) {
622
+ $core = Dispatcher::component( 'Extension_NewRelic_Core' );
623
+ $a = $core->get_effective_browser_application();
624
+ if ( isset( $a['name'] ) )
625
+ return $a['name'];
626
+
627
+ return '?';
628
+ }
629
+
630
+ return $config->get_string( array( 'newrelic', 'apm.application_name' ) );
631
+ }
632
+ }
Extension_NewRelic_Widget.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extension_NewRelic_Widget {
7
+ private $_config = null;
8
+
9
+
10
+
11
+ static public function w3tc_ajax() {
12
+ $o = new Extension_NewRelic_Widget();
13
+
14
+ add_action( 'w3tc_ajax_newrelic_widgetdata_basic', array(
15
+ $o, 'w3tc_ajax_newrelic_widgetdata_basic' ) );
16
+ add_action( 'w3tc_ajax_newrelic_widgetdata_pageloads', array(
17
+ $o, 'w3tc_ajax_newrelic_widgetdata_pageloads' ) );
18
+ add_action( 'w3tc_ajax_newrelic_widgetdata_webtransactions', array(
19
+ $o, 'w3tc_ajax_newrelic_widgetdata_webtransactions' ) );
20
+ add_action( 'w3tc_ajax_newrelic_widgetdata_dbtimes', array(
21
+ $o, 'w3tc_ajax_newrelic_widgetdata_dbtimes' ) );
22
+ }
23
+
24
+
25
+
26
+ static public function admin_init_w3tc_dashboard() {
27
+ $o = new Extension_NewRelic_Widget();
28
+ $o->_config = Dispatcher::config();
29
+
30
+ add_action( 'w3tc_widget_setup', array( $o, 'wp_dashboard_setup' ) );
31
+ add_action( 'w3tc_network_dashboard_setup',
32
+ array( $o, 'wp_dashboard_setup' ) );
33
+
34
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
35
+ $view_application = $nerser->get_effective_application_id();
36
+ $new_relic_configured =
37
+ ( $o->_config->get_string( array( 'newrelic', 'api_key' ) ) &&
38
+ $view_application != 0 );
39
+ $monitoring_type = $o->_config->get_string( array(
40
+ 'newrelic', 'monitoring_type' ) );
41
+ if ( $monitoring_type != 'browser' ) {
42
+ wp_enqueue_script( 'w3tc-widget-newrelic',
43
+ plugins_url( 'Extension_NewRelic_Widget_View.js', W3TC_FILE ),
44
+ array(), W3TC_VERSION );
45
+ }
46
+
47
+
48
+ wp_enqueue_style( 'w3tc-widget-newrelic',
49
+ plugins_url( 'Extension_NewRelic_Widget_View.css', W3TC_FILE ),
50
+ array(), W3TC_VERSION );
51
+ }
52
+
53
+
54
+
55
+ /**
56
+ * Dashboard setup action
57
+ *
58
+ * @return void
59
+ */
60
+ function wp_dashboard_setup() {
61
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
62
+
63
+ $view = '';
64
+
65
+ $view_application = $nerser->get_effective_application_id();
66
+ $new_relic_configured =
67
+ ( $this->_config->get_string( array( 'newrelic', 'api_key' ) ) &&
68
+ $view_application != 0 );
69
+
70
+ if ( $new_relic_configured ) {
71
+ $view_vis = sprintf(
72
+ "https://rpm.newrelic.com/accounts/%d/applications/%d",
73
+ $nerser->get_account_id(),
74
+ $nerser->get_effective_application_id() );
75
+ $view = '<div class="w3tc-widget-text"><a href="' .
76
+ $view_vis . '">' .
77
+ __( 'view visualizations', 'w3-total-cache' ) . '</a></div>';
78
+ }
79
+
80
+ Util_Widget::add( 'w3tc_new_relic',
81
+ '<div class="w3tc-widget-newrelic-logo"></div>' . $view,
82
+ array( $this, 'widget_new_relic' ),
83
+ Util_Ui::admin_url( 'admin.php?page=w3tc_general#monitoring' ),
84
+ 'normal' );
85
+ }
86
+
87
+
88
+
89
+ /**
90
+ * Loads and configured New Relic widget to be used in WP Dashboards.
91
+ *
92
+ * @param unknown $widget_id
93
+ * @param array $form_inputs
94
+ */
95
+ function widget_new_relic( $widget_id, $form_inputs = array() ) {
96
+ $nerser = Dispatcher::component( 'Extension_NewRelic_Service' );
97
+ $view_application = $nerser->get_effective_application_id();
98
+ $new_relic_configured =
99
+ ( $this->_config->get_string( array( 'newrelic', 'api_key' ) ) &&
100
+ $view_application != 0 );
101
+ if ( !$new_relic_configured ) {
102
+ include W3TC_DIR . '/Extension_NewRelic_Widget_View_NotConfigured.php';
103
+ return;
104
+ }
105
+
106
+ $monitoring_type = $this->_config->get_string( array(
107
+ 'newrelic', 'monitoring_type' ) );
108
+ if ( $monitoring_type == 'browser' )
109
+ include W3TC_DIR . '/Extension_NewRelic_Widget_View_Browser.php';
110
+ else
111
+ include W3TC_DIR . '/Extension_NewRelic_Widget_View_Apm.php';
112
+ }
113
+
114
+
115
+
116
+ /**
117
+ * Gives data for widget content
118
+ */
119
+ public function w3tc_ajax_newrelic_widgetdata_basic() {
120
+ // cache status for some small time
121
+ $response = get_transient( 'w3tc_nr_widgetdata_basic' );
122
+ $response = @json_decode( $response, true );
123
+ if ( is_array( $response ) && isset( $response['time'] ) &&
124
+ $response['time'] >= time() - 60 ) {
125
+ echo json_encode( $response );
126
+ return;
127
+ }
128
+
129
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
130
+ $verify_running = $service->verify_running();
131
+
132
+ $response = array(
133
+ 'time' => time()
134
+ );
135
+
136
+ if ( !is_array( $verify_running ) )
137
+ $response['php_agent'] = '<span class="w3tc-enabled">enabled</span>';
138
+ else
139
+ $response['php_agent'] = '<span class="w3tc-disabled">disabled</span>';
140
+
141
+ try {
142
+ $subscription = $service->get_subscription();
143
+ $response['subscription_level'] = $subscription['product-name'];
144
+
145
+ $summary = $service->get_application_summary();
146
+ $this->_fill( $response, 'apdex', $summary, 'Apdex' );
147
+ $this->_fill( $response, 'application_busy', $summary,
148
+ 'Application Busy' );
149
+ $this->_fill( $response, 'error_rate', $summary, 'Error Rate' );
150
+ $this->_fill( $response, 'throughput', $summary, 'Throughput' );
151
+ $this->_fill( $response, 'errors', $summary, 'Errors' );
152
+ $this->_fill( $response, 'response_time', $summary, 'Response Time' );
153
+ $this->_fill( $response, 'db', $summary, 'DB' );
154
+ $this->_fill( $response, 'cpu', $summary, 'CPU' );
155
+ $this->_fill( $response, 'memory', $summary, 'Memory' );
156
+
157
+ $can_use_metrics = $service->can_get_metrics();
158
+ if ( $can_use_metrics ) {
159
+ $dashboard_metrics = $service->get_dashboard_metrics();
160
+ $this->_fill_avg( $response, 'enduser', $dashboard_metrics,
161
+ 'EndUser' );
162
+ $this->_fill_avg( $response, 'webtransaction', $dashboard_metrics,
163
+ 'WebTransaction' );
164
+ $this->_fill_avg( $response, 'database', $dashboard_metrics,
165
+ 'Database' );
166
+ }
167
+
168
+ // load data for notification here too
169
+ $pl = $service->get_frontend_response_time();
170
+ update_option( 'w3tc_nr_frontend_response_time', $pl );
171
+ } catch ( \Exception $ex ) {
172
+ }
173
+
174
+ set_transient( 'w3tc_nr_widgetdata_basic', json_encode( $response ), 60 );
175
+ echo json_encode( $response );
176
+ }
177
+
178
+
179
+
180
+ public function w3tc_ajax_newrelic_widgetdata_pageloads() {
181
+ $response = array(
182
+ 'content' => '<div class="w3tcnr_topfive_message">No data available</div>'
183
+ );
184
+
185
+ try {
186
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
187
+ $can_use_metrics = $service->can_get_metrics();
188
+ if ( $can_use_metrics ) {
189
+ $metric_slow_pages = $service->get_slowest_page_load();
190
+ if ( count( $metric_slow_pages ) > 0 ) {
191
+ $s = '<table class="w3tcnr_slowest">';
192
+
193
+ foreach ( $metric_slow_pages as $transaction => $time ) {
194
+ $s .= '<tr><td><span>' . $transaction .
195
+ '</span></td><td>' . Util_Ui::secs_to_time( $time ) .
196
+ '</td></tr>';
197
+ }
198
+
199
+ $s .= '</table>';
200
+ $response['content'] = $s;
201
+ }
202
+ }
203
+ } catch ( \Exception $e ) {
204
+ $response['content'] = '<div class="w3tcnr_topfive_message">Error occurred</div>';
205
+ }
206
+
207
+ echo json_encode( $response );
208
+ }
209
+
210
+
211
+
212
+ public function w3tc_ajax_newrelic_widgetdata_webtransactions() {
213
+ $response = array(
214
+ 'content' => '<div class="w3tcnr_topfive_message">No data available</div>'
215
+ );
216
+
217
+ try {
218
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
219
+ $can_use_metrics = $service->can_get_metrics();
220
+ if ( $can_use_metrics ) {
221
+ $metric_slow = $service->get_slowest_webtransactions();
222
+ if ( count( $metric_slow ) > 0 ) {
223
+ $s = '<table class="w3tcnr_slowest">';
224
+
225
+ foreach ( $metric_slow as $transaction => $time ) {
226
+ $s .= '<tr><td><span>' . $transaction .
227
+ '</span></td><td>' . Util_Ui::secs_to_time( $time ) .
228
+ '</td></tr>';
229
+ }
230
+
231
+ $s .= '</table>';
232
+ $response['content'] = $s;
233
+ }
234
+ }
235
+ } catch ( \Exception $e ) {
236
+ $response['content'] = '<div class="w3tcnr_topfive_message">Error occurred</div>';
237
+ }
238
+
239
+ echo json_encode( $response );
240
+ }
241
+
242
+
243
+
244
+
245
+ public function w3tc_ajax_newrelic_widgetdata_dbtimes() {
246
+ $response = array(
247
+ 'content' => '<div class="w3tcnr_topfive_message">No data available</div>'
248
+ );
249
+
250
+ try {
251
+ $service = Dispatcher::component( 'Extension_NewRelic_Service' );
252
+ $can_use_metrics = $service->can_get_metrics();
253
+ if ( $can_use_metrics ) {
254
+ $metric_slow = $service->get_slowest_database();
255
+ if ( count( $metric_slow ) > 0 ) {
256
+ $s = '<table class="w3tcnr_slowest">';
257
+
258
+ foreach ( $metric_slow as $transaction => $time ) {
259
+ $s .= '<tr><td><span>' . $transaction .
260
+ '</span></td><td>' . Util_Ui::secs_to_time( $time ) .
261
+ '</td></tr>';
262
+ }
263
+
264
+ $s .= '</table>';
265
+ $response['content'] = $s;
266
+ }
267
+ }
268
+ } catch ( \Exception $e ) {
269
+ $response['content'] = '<div class="w3tcnr_topfive_message">Error occurred</div>';
270
+ }
271
+
272
+ echo json_encode( $response );
273
+ }
274
+
275
+
276
+
277
+ private function _fill( &$response, $response_key, $summary, $summary_key ) {
278
+ if ( isset( $summary[$summary_key] ) )
279
+ $response[$response_key] = $summary[$summary_key];
280
+ }
281
+
282
+
283
+
284
+ private function _fill_avg( &$response, $response_key, $metrics, $metric_key ) {
285
+ if ( !isset( $metrics[$metric_key] ) )
286
+ return;
287
+
288
+ $data = $metrics[$metric_key];
289
+ $response[$response_key] = Util_Ui::secs_to_time(
290
+ array_shift( $data[0] )->average_response_time
291
+ );
292
+ }
293
+ }
Extension_NewRelic_Widget_View.css ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #new-relic-widget ul li, .maxcdn-netdna-widget-base ul li {
2
+ margin-bottom: 3px;
3
+ padding: 0;
4
+ }
5
+ #new-relic-widget ul li span, .maxcdn-netdna-widget-base .summary ul li span{
6
+ display: block;
7
+ font-weight: bold;
8
+ width: 110px;
9
+ float:left;
10
+ }
11
+ #new-relic-top-list {
12
+ margin-left: -10px;
13
+ margin-right: -10px;
14
+ }
15
+ #new-relic-top-list h4 {
16
+ padding-left: 10px;
17
+ }
18
+ #new-relic-widget #new-relic-summary{
19
+ display: block;
20
+ float:left;
21
+ }
22
+ #new-relic-widget h4 {
23
+ font-size:13px;
24
+ margin: 0 0 10px;
25
+ }
26
+ #new-relic-widget h5 {
27
+ font-size:12px;
28
+ font-weight: bold;
29
+ margin-top: 10px;
30
+ margin-left: 0;
31
+ margin-bottom: 0;
32
+ border-top: 1px solid #d2d2d2;
33
+ border-bottom: 1px solid #d2d2d2;
34
+ cursor: pointer;
35
+ padding: 5px 10px;
36
+
37
+ }
38
+
39
+ #new-relic-widget h5 .handlediv {
40
+ display: block;
41
+ background: url(pub/img/open.png) 0 2px no-repeat;
42
+ width: 14px;
43
+ height: 14px;
44
+ position: relative;
45
+ }
46
+
47
+ #new-relic-widget h5 .handlediv.close {
48
+ display: block;
49
+ background: url(pub/img/close.png) 0 6px no-repeat;
50
+ width: 14px;
51
+ height: 14px;
52
+ position: relative;
53
+ }
54
+
55
+ #new-relic-extra-metrics {
56
+ float:left;
57
+ }
58
+ #new-relic-top-list {
59
+ clear:both;
60
+ }
61
+ #new-relic-top-list h4 {
62
+ padding-top: 20px;
63
+ color: #8F8F8F;
64
+ }
65
+
66
+ .w3tcnr_slowest th{
67
+ text-align: left;
68
+ }
69
+ .w3tcnr_slowest td {
70
+ font-family: Arial, Verdana, sans-serif;
71
+ border:none;
72
+ padding: 0 0 0 10px;
73
+ }
74
+
75
+ .w3tcnr_topfive_message {
76
+ padding: 10px;
77
+ }
Extension_NewRelic_Widget_View.js ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ function w3tcnr_load_basic() {
3
+ $('.w3tcnr_loading').removeClass('w3tc_hidden');
4
+ $('.w3tcnr_content').addClass('w3tc_hidden');
5
+ $('.w3tcnr_error').addClass('w3tc_none');
6
+
7
+ $.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
8
+ '&w3tc_action=newrelic_widgetdata_basic',
9
+ function(data) {
10
+ for (p in data) {
11
+ var v = data[p];
12
+ jQuery('.w3tcnr_' + p).html(v);
13
+ }
14
+
15
+ $('.w3tcnr_content').removeClass('w3tc_hidden');
16
+ $('.w3tcnr_loading').addClass('w3tc_hidden');
17
+ }
18
+ ).fail(function() {
19
+ $('.w3tcnr_error').removeClass('w3tc_none');
20
+ $('.w3tcnr_content').addClass('w3tc_hidden');
21
+ $('.w3tcnr_loading').addClass('w3tc_hidden');
22
+ });
23
+ }
24
+
25
+
26
+
27
+ function w3tcnr_load_topfive(action, selector) {
28
+ $.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
29
+ '&w3tc_action=' + action,
30
+ function(data) {
31
+ $(selector).html(data.content);
32
+
33
+ // resize outer window to newly grown widget
34
+ jQuery('#normal-sortables').masonry({
35
+ itemSelector: '.postbox'
36
+ });
37
+ }
38
+ ).fail(function() {
39
+ $(selector).html('<div class="w3tcnr_topfive_message">Request failed</div>');
40
+ });
41
+ }
42
+
43
+
44
+ var nr_widget = jQuery('#new-relic-widget');
45
+ nr_widget.find('div.top-five').hide();
46
+ $('.w3tcnr-header-pageloads').click(function() {
47
+ jQuery(this).find('div').toggleClass('close');
48
+ jQuery(this).parents('.wrapper').find("div.top-five").toggle();
49
+
50
+ w3tcnr_load_topfive('newrelic_widgetdata_pageloads',
51
+ '.w3tcnr_pageloads');
52
+ });
53
+ $('.w3tcnr-header-webtransactions').click(function() {
54
+ jQuery(this).find('div').toggleClass('close');
55
+ jQuery(this).parents('.wrapper').find("div.top-five").toggle();
56
+
57
+ w3tcnr_load_topfive('newrelic_widgetdata_webtransactions',
58
+ '.w3tcnr_webtransactions');
59
+ });
60
+ $('.w3tcnr-header-dbtimes').click(function() {
61
+ jQuery(this).find('div').toggleClass('close');
62
+ jQuery(this).parents('.wrapper').find("div.top-five").toggle();
63
+
64
+ w3tcnr_load_topfive('newrelic_widgetdata_dbtimes',
65
+ '.w3tcnr_dbtimes');
66
+ });
67
+
68
+
69
+ w3tcnr_load_basic();
70
+ });
Extension_NewRelic_Widget_View_Apm.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <div id="new-relic-widget">
9
+ <div class="w3tcnr_loading w3tc_loading w3tc_hidden">Loading...</div>
10
+ <div class="w3tcnr_error w3tc_none">An error occurred</div>
11
+
12
+ <div class="w3tcnr_content w3tc_hidden">
13
+ <div id="new-relic-summary">
14
+ <h4><?php _e( 'Overview', 'w3-total-cache' )?></h4>
15
+ <ul>
16
+ <li><span>Apdex: </span><span class="w3tcnr_apdex">N/A</span></li>
17
+ <li><span>Application Busy: </span><span class="w3tcnr_application_busy">N/A</span></li>
18
+ <li><span>Error Rate: </span><span class="w3tcnr_error_rate">N/A</span></li>
19
+ <li><span>Throughput: </span><span class="w3tcnr_throughput">N/A</span></li>
20
+ <li><span>Errors: </span><span class="w3tcnr_errors">N/A</span></li>
21
+ <li><span>Response Time: </span><span class="w3tcnr_response_time">N/A</span></li>
22
+ <li><span>DB: </span><span class="w3tcnr_db">N/A</span></li>
23
+ <li><span>CPU: </span><span class="w3tcnr_cpu">N/A</span></li>
24
+ <li><span>Memory: </span><span class="w3tcnr_memory">N/A</span></li>
25
+ </ul>
26
+ </div>
27
+ <div id="new-relic-extra-metrics">
28
+ <h4><?php _e( 'Average times', 'w3-total-cache' )?></h4>
29
+ <ul>
30
+ <li><span>Page load time: </span><span class="w3tcnr_enduser">N/A</span></li>
31
+ <li><span>Web Transaction: </span><span class="w3tcnr_webtransaction">N/A</span></li>
32
+ <li><span>Database: </span><span class="w3tcnr_database">N/A</span></li>
33
+ </ul>
34
+ <div style="clear:both"></div>
35
+ </div>
36
+ <div id="new-relic-top-list">
37
+ <h4><?php _e( 'Top 5 slowest times', 'w3-total-cache' )?></h4>
38
+ <div class="wrapper">
39
+ <h5 class="w3tcnr-header-pageloads"><?php _e( 'Page load times', 'w3-total-cache' )?><div class="handlediv open" title="Click to toggle"><br></div></h5>
40
+ <div class="top-five w3tcnr_pageloads">
41
+ <div class="w3tcnr_topfive_message">Loading...</div>
42
+ </div>
43
+ </div>
44
+ <div class="wrapper">
45
+ <h5 class="w3tcnr-header-webtransactions"><?php _e( 'Web Transaction times', 'w3-total-cache' )?><div class="handlediv open" title="Click to toggle"><br></div></h5>
46
+ <div class="top-five w3tcnr_webtransactions">
47
+ <div class="w3tcnr_topfive_message">Loading...</div>
48
+ </div>
49
+ </div>
50
+ <div class="wrapper">
51
+ <h5 class="w3tcnr-header-dbtimes"><?php _e( 'Database times', 'w3-total-cache' )?><div class="handlediv open" title="Click to toggle"><br></div></h5>
52
+ <div id="w3tc-database-times" class="top-five w3tcnr_dbtimes">
53
+ <div class="w3tcnr_topfive_message">Loading...</div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ <div style="clear:both"></div>
58
+ <hr>
59
+ <p>
60
+ <?php _e( 'PHP agent:', 'w3-total-cache' )?>
61
+ <span class="w3tcnr_php_agent">N/A</span>
62
+ <br />
63
+
64
+ <?php _e( 'Subscription level:', 'w3-total-cache' )?>
65
+ <strong class="w3tcnr_subscription_level">N/A</strong>
66
+ </p>
67
+ </div>
68
+ </div>
Extension_NewRelic_Widget_View_Browser.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php _e( 'Metrics are not available for browser applications', 'w3-total-cache' )?>
9
+ <p>
10
+ <a href="<?php echo esc_attr( NEWRELIC_SIGNUP_URL ); ?>" target="_blank">
11
+ <?php _e( 'Upgrade your New Relic account to enable more metrics.', 'w3-total-cache' )?>
12
+ </a>
13
+ </p>
Extension_NewRelic_Widget_View_NotConfigured.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ ?>
8
+ <?php _e( 'You have not configured API key and Account Id.', 'w3-total-cache' )?>
Extension_WordPressSeo_Plugin.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_WordPressSeo_Plugin {
5
+ private $_config;
6
+
7
+ function __construct() {
8
+ $this->_config = Dispatcher::config();
9
+ }
10
+
11
+ public function run() {
12
+ if ( $this->_config->get_boolean( 'cdn.enabled' ) ) {
13
+ add_filter( 'wpseo_xml_sitemap_img_src', array(
14
+ $this, 'wpseo_cdn_filter' ) );
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Hook into Yoast SEO sitemap image filter.
20
+ *
21
+ * @param unknown $uri
22
+ * @return string
23
+ */
24
+ public function wpseo_cdn_filter( $uri ) {
25
+ $common = Dispatcher::component( 'Cdn_Core' );
26
+ $cdn = $common->get_cdn();
27
+ $parsed = parse_url( $uri );
28
+ $path = $parsed['path'];
29
+ $remote_path = $common->uri_to_cdn_uri( $path );
30
+ $new_url = $cdn->format_url( $remote_path );
31
+
32
+ return $new_url;
33
+ }
34
+ }
35
+
36
+
37
+
38
+ $p = new Extension_WordPressSeo_Plugin();
39
+ $p->run();
40
+
41
+ if ( is_admin() ) {
42
+ $p = new Extension_WordPressSeo_Plugin_Admin();
43
+ $p->run();
44
+ }
Extension_WordPressSeo_Plugin_Admin.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_WordPressSeo_Plugin_Admin {
5
+ function run() {
6
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
7
+ add_filter( 'w3tc_extension_plugin_links_wordpress-seo', array( $this, 'remove_settings' ) );
8
+ add_action( 'w3tc_activate_extension_wordpress-seo', array( $this, 'activate' ) );
9
+ add_action( 'w3tc_deactivate_extension_wordpress-seo', array( $this, 'deactivate' ) );
10
+ }
11
+
12
+ public function admin_init() {
13
+ $config = Dispatcher::config();
14
+ $groups = $config->get_array( 'mobile.rgroups' );
15
+ if ( Util_Environment::is_w3tc_edge( $config ) &&
16
+ isset( $groups['google'] ) &&
17
+ sizeof( $groups['google']['agents'] ) == 1 &&
18
+ $groups['google']['agents'][0] == 'googlebot' ) {
19
+ w3tc_delete_user_agent_group( 'google' );
20
+ }
21
+ }
22
+
23
+ /**
24
+ *
25
+ *
26
+ * @param unknown $links
27
+ * @return mixed
28
+ */
29
+ public function remove_settings( $links ) {
30
+ array_pop( $links );
31
+ return $links;
32
+ }
33
+
34
+ /**
35
+ *
36
+ *
37
+ * @param unknown $extensions
38
+ * @param Config $config
39
+ * @return mixed
40
+ */
41
+ static public function w3tc_extensions( $extensions, $config ) {
42
+ $message = array();
43
+ if ( !self::criteria_match() )
44
+ $message[] = 'Optimizes "Yoast SEO" plugin, which is not active';
45
+
46
+ $extensions['wordpress-seo'] = array (
47
+ 'name' => 'WordPress SEO by Yoast',
48
+ 'author' => 'W3 EDGE',
49
+ 'description' => __( 'Configures W3 Total Cache to comply with Yoast SEO requirements automatically.', 'w3-total-cache' ),
50
+
51
+ 'author_uri' => 'https://www.w3-edge.com/',
52
+ 'extension_uri' => 'https://www.w3-edge.com/',
53
+ 'extension_id' => 'wordpress-seo',
54
+ 'version' => '0.1',
55
+ 'enabled' => self::criteria_match(),
56
+ 'requirements' => implode( ', ', $message ),
57
+ 'path' => 'w3-total-cache/Extension_WordPressSeo_Plugin.php'
58
+ );
59
+
60
+ return $extensions;
61
+ }
62
+
63
+ /**
64
+ * called from outside, since can show notice even when extension is not active
65
+ */
66
+ static public function w3tc_extensions_hooks( $hooks ) {
67
+ if ( !self::show_notice() )
68
+ return $hooks;
69
+
70
+ if ( !isset( $hooks['filters']['w3tc_notes'] ) )
71
+ $hooks['filters']['w3tc_notes'] = array();
72
+
73
+ $hooks['filters']['w3tc_notes'][] = 'w3tc_notes_wordpress_seo';
74
+ return $hooks;
75
+ }
76
+
77
+ static private function show_notice() {
78
+ $config = Dispatcher::config();
79
+ if ( $config->is_extension_active( 'wordpress-seo' ) )
80
+ return false;
81
+
82
+ if ( !self::criteria_match() )
83
+ return false;
84
+
85
+ $state = Dispatcher::config_state();
86
+ if ( $state->get_boolean( 'wordpress_seo.hide_note_suggest_activation' ) )
87
+ return false;
88
+
89
+ return true;
90
+ }
91
+
92
+ static public function w3tc_notes_wordpress_seo( $notes ) {
93
+ if ( !self::show_notice() )
94
+ return $notes;
95
+
96
+ $extension_id = 'wordpress-seo';
97
+
98
+ $notes[$extension_id] = sprintf(
99
+ __( 'It appears that activating the <a href="%s">Yoast SEO</a> extension for W3 Total Cache will be helpful for your site. <a class="button" href="%s">Click here</a> to try it. %s',
100
+ 'w3-total-cache' ),
101
+ Util_Ui::admin_url( 'admin.php?page=w3tc_extensions#' . $extension_id ),
102
+ Util_Ui::url( array( 'w3tc_extensions_activate' => $extension_id ) ),
103
+ Util_Ui::button_link(
104
+ __( 'Hide this message', 'w3-total-cache' ),
105
+ Util_Ui::url( array(
106
+ 'w3tc_default_config_state' => 'y',
107
+ 'key' => 'wordpress_seo.hide_note_suggest_activation',
108
+ 'value' => 'true' ) ) ) );
109
+
110
+ return $notes;
111
+ }
112
+
113
+ static private function criteria_match() {
114
+ return defined( 'WPSEO_VERSION' );
115
+ }
116
+
117
+ public function activate() {
118
+ try {
119
+ $config = Dispatcher::config();
120
+ $config->set( 'pgcache.prime.enabled', true );
121
+ $config->set( 'pgcache.prime.sitemap', '/sitemap_index.xml' );
122
+ $config->save();
123
+ } catch ( \Exception $ex ) {}
124
+ }
125
+
126
+ public function deactivate() {
127
+ try {
128
+ $config = Dispatcher::config();
129
+ $config->set( 'pgcache.prime.enabled', false );
130
+ $config->save();
131
+ } catch ( \Exception $ex ) {}
132
+ }
133
+ }
Extension_Wpml_Plugin.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_Wpml_Plugin {
5
+ private $_config;
6
+
7
+ function __construct() {
8
+ $this->_config = Dispatcher::config();
9
+ }
10
+
11
+ public function run() {
12
+ if ( Util_Environment::is_w3tc_pro( $this->_config ) ) {
13
+ add_filter( 'w3tc_url_to_docroot_filename',
14
+ array( $this, 'w3tc_url_to_docroot_filename' ) );
15
+ }
16
+ }
17
+
18
+
19
+
20
+ public function w3tc_url_to_docroot_filename( $data ) {
21
+ $home_url = $data['home_url'];
22
+
23
+ if ( substr( $data['url'], 0, strlen( $home_url ) ) != $home_url ) {
24
+ $data['home_url'] = get_option( 'home' );
25
+ }
26
+
27
+ return $data;
28
+ }
29
+ }
30
+
31
+
32
+
33
+ $p = new Extension_Wpml_Plugin();
34
+ $p->run();
35
+
36
+ if ( is_admin() ) {
37
+ $p = new Extension_Wpml_Plugin_Admin();
38
+ $p->run();
39
+ }
Extension_Wpml_Plugin_Admin.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class Extension_Wpml_Plugin_Admin {
5
+ function run() {
6
+ add_filter( 'w3tc_notes', array( $this, 'w3tc_notes' ) );
7
+ }
8
+
9
+ function w3tc_notes( $notes ) {
10
+ $config = Dispatcher::config();
11
+ $settings = get_option( 'icl_sitepress_settings' );
12
+
13
+ if ( $config->get_boolean( 'pgcache.enabled' ) &&
14
+ $config->get_string( 'pgcache.engine' ) == 'file_generic' &&
15
+ isset( $settings[ 'language_negotiation_type' ] ) &&
16
+ $settings[ 'language_negotiation_type' ] == 3 ) {
17
+
18
+ $state = Dispatcher::config_state();
19
+
20
+ if ( !$state->get_boolean( 'wpml.hide_note_language_negotiation_type' ) ) {
21
+ $notes[] = sprintf(
22
+ __( 'W3 Total Cache\'s Page caching can not work effectively when WPML Language URL format "Language name added as a parameter" used. Please consider another URL format, you may change it at WPML -&gt; Languages page. %s' ,
23
+ 'w3-total-cache' ),
24
+ Util_Ui::button_hide_note2( array(
25
+ 'w3tc_default_config_state' => 'y',
26
+ 'key' => 'wpml.hide_note_language_negotiation_type',
27
+ 'value' => 'true' ) ) );
28
+ }
29
+
30
+ }
31
+
32
+ return $notes;
33
+ }
34
+
35
+ static public function w3tc_extensions( $extensions, $config ) {
36
+ $base_plugin_active = self::base_plugin_active();
37
+ $enabled = $base_plugin_active;
38
+ $disabled_message = '';
39
+
40
+ $requirements = array();
41
+ if ( !$base_plugin_active )
42
+ $requirements[] = 'Ensure "WPML" plugin compatibility, which is not active';
43
+ if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) ) {
44
+ $enabled = false;
45
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
46
+ $disabled_message = '<a href="#" class="button-buy-plugin">upgrade</a>';
47
+ }
48
+
49
+ $extensions['wpml'] = array(
50
+ 'name' => 'WPML.org',
51
+ 'author' => 'W3 EDGE',
52
+ 'description' => __( 'Configures W3 Total Cache to comply with WPML requirements automatically.',
53
+ 'w3-total-cache' ),
54
+ 'author_uri' => 'https://www.w3-edge.com/',
55
+ 'extension_uri' => 'https://www.w3-edge.com/',
56
+ 'extension_id' => 'wpml',
57
+ 'version' => '0.1',
58
+ 'enabled' => $enabled,
59
+ 'disabled_message' => $disabled_message,
60
+ 'requirements' => implode( ', ', $requirements ),
61
+ 'path' => 'w3-total-cache/Extension_Wpml_Plugin.php'
62
+ );
63
+
64
+
65
+
66
+ return $extensions;
67
+ }
68
+
69
+ static public function base_plugin_active() {
70
+ return defined( 'ICL_SITEPRESS_VERSION' );
71
+ }
72
+
73
+ /**
74
+ * called from outside, since can show notice even when extension is not active
75
+ */
76
+ static public function w3tc_extensions_hooks( $hooks ) {
77
+ if ( !self::show_notice() )
78
+ return $hooks;
79
+
80
+ if ( !isset( $hooks['filters']['w3tc_notes'] ) )
81
+ $hooks['filters']['w3tc_notes'] = array();
82
+
83
+ $hooks['filters']['w3tc_notes'][] = 'w3tc_notes_wpml';
84
+ return $hooks;
85
+ }
86
+
87
+ static private function show_notice() {
88
+ $config = Dispatcher::config();
89
+ if ( $config->is_extension_active( 'wpml' ) )
90
+ return false;
91
+
92
+ if ( !self::base_plugin_active() )
93
+ return false;
94
+
95
+ $state = Dispatcher::config_state();
96
+ if ( $state->get_boolean( 'wpml.hide_note_suggest_activation' ) )
97
+ return false;
98
+
99
+ return true;
100
+ }
101
+
102
+ static public function w3tc_notes_wpml( $notes ) {
103
+ if ( !self::show_notice() )
104
+ return $notes;
105
+
106
+ $extension_id = 'wpml';
107
+
108
+ $config = Dispatcher::config();
109
+ if ( !Util_Environment::is_w3tc_pro( $config ) )
110
+ $activate_text = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>. ';
111
+ else {
112
+ $activate_text = sprintf( '<a class="button" href="%s">Click here</a> to try it. ',
113
+ Util_Ui::url( array( 'w3tc_extensions_activate' => $extension_id ) ) );
114
+ }
115
+
116
+ $notes[$extension_id] = sprintf(
117
+ __( 'It appears that activating the <a href="%s">WPML</a> extension for W3 Total Cache will be helpful for your site. %s%s',
118
+ 'w3-total-cache' ),
119
+ Util_Ui::admin_url( 'admin.php?page=w3tc_extensions#' . $extension_id ),
120
+ $activate_text,
121
+ Util_Ui::button_link(
122
+ __( 'Hide this message', 'w3-total-cache' ),
123
+ Util_Ui::url( array(
124
+ 'w3tc_default_config_state' => 'y',
125
+ 'key' => 'wpml.hide_note_suggest_activation',
126
+ 'value' => 'true' ) ) ) );
127
+
128
+ return $notes;
129
+ }
130
+ }
Extensions_AdminActions.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extensions_AdminActions {
7
+ function w3tc_extensions_activate() {
8
+ $config = Dispatcher::config();
9
+
10
+ $extension = Util_Request::get_string( 'w3tc_extensions_activate' );
11
+ $ext = Extensions_Util::get_extension( $config, $extension );
12
+
13
+ if ( !is_null( $ext ) ) {
14
+ if ( Extensions_Util::activate_extension( $extension, $config ) ) {
15
+ Util_Admin::redirect_with_custom_messages2( array(
16
+ 'notes' => array( sprintf(
17
+ __( 'Extension <strong>%s</strong> has been successfully activated.',
18
+ 'w3-total-cache' ),
19
+ $ext['name']
20
+ ) )
21
+ ) );
22
+ return;
23
+ }
24
+ }
25
+
26
+ Util_Admin::redirect( array() );
27
+ }
28
+ }
Extensions_Page.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class Extensions_Page extends Base_Page_Settings {
7
+ /**
8
+ * Current page
9
+ *
10
+ * @var string
11
+ */
12
+ protected $_page = 'w3tc_extensions';
13
+ protected $_active_tab;
14
+ protected $_config_settings = array();
15
+
16
+ /**
17
+ * Extensions view
18
+ *
19
+ * @return void
20
+ */
21
+ function render_content() {
22
+ $extension = '';
23
+ $extension_status = 'all';
24
+
25
+ if ( isset( $_GET['extension_status'] ) ) {
26
+ if ( in_array( $_GET['extension_status'], array( 'all', 'active', 'inactive', 'core' ) ) )
27
+ $extension_status = $_GET['extension_status'];
28
+ }
29
+
30
+ if ( isset( $_GET['extension'] ) ) {
31
+ $extension = $_GET['extension'];
32
+ }
33
+
34
+ $view = ( isset( $_GET['action'] ) && $_GET['action'] == 'view' );
35
+
36
+ $extensions_active = Extensions_Util::get_active_extensions( $this->_config );
37
+
38
+ if ( $extension && $view ) {
39
+ $all_settings = $this->_config->get_array( 'extensions.settings' );
40
+ $extensions_active = Extensions_Util::get_active_extensions( $this->_config );
41
+ $meta = $extensions_active[$extension];
42
+ $sub_view = 'settings';
43
+ } else {
44
+ $extensions_all = Extensions_Util::get_extensions( $this->_config );
45
+ $extensions_inactive = Extensions_Util::get_inactive_extensions( $this->_config );
46
+ $var = "extensions_{$extension_status}";
47
+ $extensions = $$var;
48
+ $sub_view = 'list';
49
+ $page = 1;
50
+ }
51
+
52
+ $config = Dispatcher::config();
53
+ include W3TC_INC_OPTIONS_DIR . '/extensions.php';
54
+ }
55
+
56
+ /**
57
+ * Sets default values for lacking extension meta keys
58
+ *
59
+ * @param unknown $meta
60
+ * @return array
61
+ */
62
+ function default_meta( $meta ) {
63
+ $default = array (
64
+ 'name' => '',
65
+ 'author' => '',
66
+ 'description' => '',
67
+ 'author_uri' => '',
68
+ 'extension_uri' => '',
69
+ 'extension_id' => '',
70
+ 'version' => '',
71
+ 'enabled' => true,
72
+ 'requirements' => array(),
73
+ 'core' => false,
74
+ 'path' => ''
75
+ );
76
+ return array_merge( $default, $meta );
77
+ }
78
+ }
Extensions_Plugin_Admin.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ /**
5
+ * W3 Total Cache ExtensionsAdmin plugin
6
+ */
7
+ class Extensions_Plugin_Admin {
8
+ /**
9
+ * Config
10
+ */
11
+ private $_config = null;
12
+
13
+ function __construct() {
14
+ $this->_config = Dispatcher::config();
15
+ }
16
+
17
+ /**
18
+ * Runs plugin
19
+ */
20
+ function run() {
21
+ // attach w3tc-bundled extensions
22
+ add_filter( 'w3tc_extensions', array(
23
+ '\W3TC\Extension_CloudFlare_Plugin_Admin',
24
+ 'w3tc_extensions' ),
25
+ 10, 2 );
26
+ add_filter( 'w3tc_extensions', array(
27
+ '\W3TC\Extension_FeedBurner_Plugin_Admin',
28
+ 'w3tc_extensions' ),
29
+ 10, 2 );
30
+ add_filter( 'w3tc_extensions', array(
31
+ '\W3TC\Extension_FragmentCache_Plugin_Admin',
32
+ 'w3tc_extensions' ),
33
+ 10, 2 );
34
+ add_filter( 'w3tc_extensions', array(
35
+ '\W3TC\Extension_Genesis_Plugin_Admin',
36
+ 'w3tc_extensions' ),
37
+ 10, 2 );
38
+ add_filter( 'w3tc_extensions_hooks', array(
39
+ '\W3TC\Extension_Genesis_Plugin_Admin',
40
+ 'w3tc_extensions_hooks' ) );
41
+ add_filter( 'w3tc_notes_genesis_theme', array(
42
+ '\W3TC\Extension_Genesis_Plugin_Admin',
43
+ 'w3tc_notes_genesis_theme' ) );
44
+ add_filter( 'w3tc_extensions', array(
45
+ '\W3TC\Extension_NewRelic_Plugin_Admin',
46
+ 'w3tc_extensions' ),
47
+ 10, 2 );
48
+ add_filter( 'w3tc_extensions', array(
49
+ '\W3TC\Extension_WordPressSeo_Plugin_Admin',
50
+ 'w3tc_extensions' ),
51
+ 10, 2 );
52
+ add_filter( 'w3tc_extensions_hooks', array(
53
+ '\W3TC\Extension_WordPressSeo_Plugin_Admin',
54
+ 'w3tc_extensions_hooks' ) );
55
+ add_action( 'w3tc_notes_wordpress_seo', array(
56
+ '\W3TC\Extension_WordPressSeo_Plugin_Admin',
57
+ 'w3tc_notes_wordpress_seo' ) );
58
+ add_filter( 'w3tc_extensions', array(
59
+ '\W3TC\Extension_Wpml_Plugin_Admin',
60
+ 'w3tc_extensions' ),
61
+ 10, 2 );
62
+ add_filter( 'w3tc_extensions_hooks', array(
63
+ '\W3TC\Extension_Wpml_Plugin_Admin',
64
+ 'w3tc_extensions_hooks' ) );
65
+ add_action( 'w3tc_notes_wpml', array(
66
+ '\W3TC\Extension_Wpml_Plugin_Admin',
67
+ 'w3tc_notes_wpml' ) );
68
+
69
+ add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
70
+ add_filter( 'pre_update_option_active_plugins', array(
71
+ $this, 'pre_update_option_active_plugins' ) );
72
+ add_filter( 'w3tc_admin_menu', array( $this, 'w3tc_admin_menu' ), 10000 );
73
+ add_action( 'w3tc_settings_page-w3tc_extensions',
74
+ array( $this, 'w3tc_settings_page_w3tc_extensions' ) );
75
+
76
+ if ( Util_Admin::is_w3tc_admin_page() ) {
77
+ if ( isset( $_GET['extension'] ) && isset( $_GET['action'] ) ) {
78
+ if ( in_array( $_GET['action'], array( 'activate', 'deactivate' ) ) ) {
79
+ add_action( 'init', array( $this, 'change_extension_status' ) );
80
+ }
81
+ } elseif ( isset( $_POST['checked'] ) ) {
82
+ add_action( 'admin_init', array( $this, 'change_extensions_status' ) );
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Adds menu
89
+ *
90
+ * @param unknown $menu
91
+ * @return array
92
+ */
93
+ public function w3tc_admin_menu( $menu ) {
94
+ $menu_item = array(
95
+ 'w3tc_extensions' => array(
96
+ 'page_title' => __( 'Extensions', 'w3-total-cache' ),
97
+ 'menu_text' => __( 'Extensions', 'w3-total-cache' ),
98
+ 'visible_always' => false
99
+ )
100
+ );
101
+ return array_merge( $menu, $menu_item );
102
+ }
103
+
104
+ /**
105
+ * Loads options page and corresponding view
106
+ */
107
+ public function w3tc_settings_page_w3tc_extensions() {
108
+ $o = new Extensions_Page();
109
+ $o->render_content();
110
+ }
111
+
112
+ public function pre_update_option_active_plugins( $o ) {
113
+ delete_option( 'w3tc_extensions_hooks' );
114
+
115
+ return $o;
116
+ }
117
+
118
+ public function admin_init() {
119
+ // used to load even inactive extensions if they want to
120
+ $s = get_option( 'w3tc_extensions_hooks' );
121
+ $hooks = @json_decode( $s, true );
122
+ if ( !isset( $hooks['next_check_date'] ) ||
123
+ $hooks['next_check_date'] < time() || true ) {
124
+ $hooks = array(
125
+ 'actions' => array(),
126
+ 'filters' => array(),
127
+ 'next_check_date' => time() + 24 * 60 * 60
128
+ );
129
+ $hooks = apply_filters( 'w3tc_extensions_hooks', $hooks );
130
+ update_option( 'w3tc_extensions_hooks', json_encode( $hooks ) );
131
+ }
132
+
133
+ if ( isset( $hooks['actions'] ) ) {
134
+ foreach ( $hooks['actions'] as $hook => $actions_to_call ) {
135
+ if ( is_array( $actions_to_call ) ) {
136
+ add_action( $hook, function() use ( $actions_to_call ) {
137
+ foreach ( $actions_to_call as $action )
138
+ do_action( $action );
139
+ } );
140
+ }
141
+ }
142
+ }
143
+
144
+ if ( isset( $hooks['filters'] ) ) {
145
+ foreach ( $hooks['filters'] as $hook => $filters_to_call ) {
146
+ if ( is_array( $filters_to_call ) ) {
147
+ add_filter( $hook, function( $v ) use ( $filters_to_call ) {
148
+ foreach ( $filters_to_call as $filter )
149
+ $v = apply_filters( $filter, $v );
150
+
151
+ return $v;
152
+ } );
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Alters the active state of multiple extensions
160
+ */
161
+ public function change_extensions_status() {
162
+ $extensions = Util_Request::get_array( 'checked' );
163
+ $action = Util_Request::get( 'action' );
164
+ if ( '-1' == $action )
165
+ $action = Util_Request::get( 'action2' ); // dropdown at bottom
166
+
167
+ $message = '';
168
+ if ( 'activate-selected' == $action ) {
169
+ foreach ( $extensions as $extension ) {
170
+ if ( Extensions_Util::activate_extension( $extension, $this->_config ) )
171
+ $message .= '&activated=' . $extension;
172
+ }
173
+ wp_redirect( Util_Ui::admin_url( sprintf( 'admin.php?page=w3tc_extensions%s', $message ) ) );
174
+ } elseif ( 'deactivate-selected' == $action ) {
175
+ foreach ( $extensions as $extension ) {
176
+ if ( Extensions_Util::deactivate_extension( $extension, $this->_config ) )
177
+ $message .= '&deactivated=' . $extension;
178
+ }
179
+ wp_redirect( Util_Ui::admin